home *** CD-ROM | disk | FTP | other *** search
- /* serial.c
- The serial port communication routines for Unix.
-
- Copyright (C) 1991, 1992, 1993, 1994, 1995 Ian Lance Taylor
-
- This file is part of the Taylor UUCP package.
-
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
- The author of the program may be contacted at ian@airs.com or
- c/o Cygnus Support, 48 Grove Street, Somerville, MA 02144.
- */
-
- #include "uucp.h"
-
- #if USE_RCS_ID
- const char serial_rcsid[] = "$Id: serial.c,v 1.65 1995/08/10 00:53:54 ian Rel $";
- #endif
-
- #include "uudefs.h"
- #include "uuconf.h"
- #include "system.h"
- #include "conn.h"
- #include "sysdep.h"
-
- #include <errno.h>
- #include <ctype.h>
-
- #if HAVE_SYS_PARAM_H
- #include <sys/param.h>
- #endif
-
- #if HAVE_LIMITS_H
- #include <limits.h>
- #endif
-
- #if HAVE_TLI
- #if HAVE_TIUSER_H
- #include <tiuser.h>
- #else /* ! HAVE_TIUSER_H */
- #if HAVE_XTI_H
- #include <xti.h>
- #endif /* HAVE_XTI_H */
- #endif /* ! HAVE_TIUSER_H */
- #endif /* HAVE_TLI */
-
- #if HAVE_FCNTL_H
- #include <fcntl.h>
- #else
- #if HAVE_SYS_FILE_H
- #include <sys/file.h>
- #endif
- #endif
-
- #ifndef O_RDONLY
- #define O_RDONLY 0
- #define O_WRONLY 1
- #define O_RDWR 2
- #endif
-
- #ifndef O_NOCTTY
- #define O_NOCTTY 0
- #endif
-
- #ifndef FD_CLOEXEC
- #define FD_CLOEXEC 1
- #endif
-
- #if HAVE_SYS_IOCTL_H || HAVE_TXADDCD
- #include <sys/ioctl.h>
- #endif
-
- #if HAVE_SELECT
- #if HAVE_SYS_TIME_H
- #include <sys/time.h>
- #endif
- #if HAVE_SYS_SELECT_H
- #include <sys/select.h>
- #endif
- #endif
-
- #if HAVE_TIME_H
- #if ! HAVE_SYS_TIME_H || ! HAVE_SELECT || TIME_WITH_SYS_TIME
- #include <time.h>
- #endif
- #endif
-
- #if HAVE_STRIP_BUG && HAVE_BSD_TTY
- #include <termio.h>
- #endif
-
- #if HAVE_SVR4_LOCKFILES
- /* Get the right definitions for major and minor. */
- #if MAJOR_IN_MKDEV
- #include <sys/mkdev.h>
- #endif /* MAJOR_IN_MKDEV */
- #if MAJOR_IN_SYSMACROS
- #include <sys/sysmacros.h>
- #endif /* MAJOR_IN_SYSMACROS */
- #if ! MAJOR_IN_MKDEV && ! MAJOR_IN_SYSMACROS
- #ifndef major
- #define major(i) (((i) >> 8) & 0xff)
- #endif
- #ifndef minor
- #define minor(i) ((i) & 0xff)
- #endif
- #endif /* ! MAJOR_IN_MKDEV && ! MAJOR_IN_SYSMACROS */
- #endif /* HAVE_SVR4_LOCKFILES */
-
- #if HAVE_DEV_INFO
- #include <sys/dev.h>
- #endif
-
- /* Get definitions for both O_NONBLOCK and O_NDELAY. */
- #ifndef O_NDELAY
- #ifdef FNDELAY
- #define O_NDELAY FNDELAY
- #else /* ! defined (FNDELAY) */
- #define O_NDELAY 0
- #endif /* ! defined (FNDELAY) */
- #endif /* ! defined (O_NDELAY) */
-
- #ifndef O_NONBLOCK
- #ifdef FNBLOCK
- #define O_NONBLOCK FNBLOCK
- #else /* ! defined (FNBLOCK) */
- #define O_NONBLOCK 0
- #endif /* ! defined (FNBLOCK) */
- #endif /* ! defined (O_NONBLOCK) */
-
- #if O_NDELAY == 0 && O_NONBLOCK == 0
- #error No way to do nonblocking I/O
- #endif
-
- /* Get definitions for EAGAIN, EWOULDBLOCK and ENODATA. */
- #ifndef EAGAIN
- #ifndef EWOULDBLOCK
- #define EAGAIN (-1)
- #define EWOULDBLOCK (-1)
- #else /* defined (EWOULDBLOCK) */
- #define EAGAIN EWOULDBLOCK
- #endif /* defined (EWOULDBLOCK) */
- #else /* defined (EAGAIN) */
- #ifndef EWOULDBLOCK
- #define EWOULDBLOCK EAGAIN
- #endif /* ! defined (EWOULDBLOCK) */
- #endif /* defined (EAGAIN) */
-
- #ifndef ENODATA
- #define ENODATA EAGAIN
- #endif
-
- /* Make sure we have a definition for MAX_INPUT. */
- #ifndef MAX_INPUT
- #define MAX_INPUT (256)
- #endif
-
- /* If we have the TIOCSINUSE ioctl call, we use it to lock a terminal.
- Otherwise, if we have the TIOCEXCL ioctl call, we have to open the
- terminal before we know that it is unlocked. */
- #ifdef TIOCSINUSE
- #define HAVE_TIOCSINUSE 1
- #else
- #ifdef TIOCEXCL
- #define HAVE_TIOCEXCL 1
- #endif
- #endif
-
- #if HAVE_TLI
- extern int t_errno;
- extern char *t_errlist[];
- extern int t_nerr;
- #endif
-
- /* Determine bits to clear for the various terminal control fields for
- HAVE_SYSV_TERMIO and HAVE_POSIX_TERMIOS. */
-
- /* These fields are defined on some systems, and I am told that it
- does not hurt to clear them, and it sometimes helps. */
- #ifndef IMAXBEL
- #define IMAXBEL 0
- #endif
-
- #ifndef PENDIN
- #define PENDIN 0
- #endif
-
- #if HAVE_SYSV_TERMIO
- #define ICLEAR_IFLAG (IGNBRK | BRKINT | IGNPAR | PARMRK | INPCK \
- | ISTRIP | INLCR | IGNCR | ICRNL | IUCLC \
- | IXON | IXANY | IXOFF | IMAXBEL)
- #define ICLEAR_OFLAG (OPOST | OLCUC | ONLCR | OCRNL | ONOCR | ONLRET \
- | OFILL | OFDEL | NLDLY | CRDLY | TABDLY | BSDLY \
- | VTDLY | FFDLY)
- #define ICLEAR_CFLAG (CBAUD | CSIZE | PARENB | PARODD)
- #define ISET_CFLAG (CS8 | CREAD | HUPCL)
- #define ICLEAR_LFLAG (ISIG | ICANON | XCASE | ECHO | ECHOE | ECHOK \
- | ECHONL | NOFLSH | PENDIN)
- #endif
- #if HAVE_POSIX_TERMIOS
- #define ICLEAR_IFLAG (BRKINT | ICRNL | IGNBRK | IGNCR | IGNPAR \
- | INLCR | INPCK | ISTRIP | IXOFF | IXON \
- | PARMRK | IMAXBEL)
- #define ICLEAR_OFLAG (OPOST)
- #define ICLEAR_CFLAG (CSIZE | PARENB | PARODD)
- #define ISET_CFLAG (CS8 | CREAD | HUPCL)
- #define ICLEAR_LFLAG (ECHO | ECHOE | ECHOK | ECHONL | ICANON | IEXTEN \
- | ISIG | NOFLSH | TOSTOP | PENDIN)
- #endif
-
- enum tclocal_setting
- {
- SET_CLOCAL,
- CLEAR_CLOCAL,
- IGNORE_CLOCAL
- };
-
- /* Local functions. */
-
- static RETSIGTYPE usalarm P((int isig));
- static boolean fsserial_init P((struct sconnection *qconn,
- const struct sconncmds *qcmds,
- const char *zdevice));
- static void usserial_free P((struct sconnection *qconn));
- static boolean fsserial_lockfile P((boolean flok,
- const struct sconnection *));
- static boolean fsserial_lock P((struct sconnection *qconn,
- boolean fin));
- static boolean fsserial_unlock P((struct sconnection *qconn));
- static boolean fsserial_open P((struct sconnection *qconn, long ibaud,
- boolean fwait, enum tclocal_setting tlocal));
- static boolean fsstdin_open P((struct sconnection *qconn, long ibaud,
- boolean fwait));
- static boolean fsmodem_open P((struct sconnection *qconn, long ibaud,
- boolean fwait));
- static boolean fsdirect_open P((struct sconnection *qconn, long ibaud,
- boolean fwait));
- static boolean fsblock P((struct ssysdep_conn *q, boolean fblock));
- static boolean fsserial_close P((struct ssysdep_conn *q));
- static boolean fsstdin_close P((struct sconnection *qconn,
- pointer puuconf,
- struct uuconf_dialer *qdialer,
- boolean fsuccess));
- static boolean fsmodem_close P((struct sconnection *qconn,
- pointer puuconf,
- struct uuconf_dialer *qdialer,
- boolean fsuccess));
- static boolean fsdirect_close P((struct sconnection *qconn,
- pointer puuconf,
- struct uuconf_dialer *qdialer,
- boolean fsuccess));
- static boolean fsserial_break P((struct sconnection *qconn));
- static boolean fsstdin_break P((struct sconnection *qconn));
- static boolean fsserial_set P((struct sconnection *qconn,
- enum tparitysetting tparity,
- enum tstripsetting tstrip,
- enum txonxoffsetting txonxoff));
- static boolean fsstdin_set P((struct sconnection *qconn,
- enum tparitysetting tparity,
- enum tstripsetting tstrip,
- enum txonxoffsetting txonxoff));
- static boolean fsmodem_carrier P((struct sconnection *qconn,
- boolean fcarrier));
- static boolean fsserial_hardflow P((struct sconnection *qconn,
- boolean fhardflow));
- static boolean fsrun_chat P((int oread, int owrite, char **pzprog));
- static long isserial_baud P((struct sconnection *qconn));
-
- /* The command table for standard input ports. */
-
- static const struct sconncmds sstdincmds =
- {
- usserial_free,
- NULL, /* pflock */
- NULL, /* pfunlock */
- fsstdin_open,
- fsstdin_close,
- NULL, /* pfdial */
- fsdouble_read,
- fsdouble_write,
- fsysdep_conn_io,
- fsstdin_break,
- fsstdin_set,
- NULL, /* pfcarrier */
- fsdouble_chat,
- isserial_baud
- };
-
- /* The command table for modem ports. */
-
- static const struct sconncmds smodemcmds =
- {
- usserial_free,
- fsserial_lock,
- fsserial_unlock,
- fsmodem_open,
- fsmodem_close,
- fmodem_dial,
- fsysdep_conn_read,
- fsysdep_conn_write,
- fsysdep_conn_io,
- fsserial_break,
- fsserial_set,
- fsmodem_carrier,
- fsysdep_conn_chat,
- isserial_baud
- };
-
- /* The command table for direct ports. */
-
- static const struct sconncmds sdirectcmds =
- {
- usserial_free,
- fsserial_lock,
- fsserial_unlock,
- fsdirect_open,
- fsdirect_close,
- NULL, /* pfdial */
- fsysdep_conn_read,
- fsysdep_conn_write,
- fsysdep_conn_io,
- fsserial_break,
- fsserial_set,
- NULL, /* pfcarrier */
- fsysdep_conn_chat,
- isserial_baud
- };
-
- /* If the system will let us set both O_NDELAY and O_NONBLOCK, we do
- so. This is because some ancient drivers on some systems appear to
- look for one but not the other. Some other systems will give an
- EINVAL error if we attempt to set both, so we use a static global
- to hold the value we want to set. If we get EINVAL, we change the
- global and try again (if some system gives an error other than
- EINVAL, the code will have to be modified). */
- static int iSunblock = O_NDELAY | O_NONBLOCK;
-
- /* This code handles SIGALRM. See the discussion above
- fsysdep_conn_read. Normally we ignore SIGALRM, but the handler
- will temporarily be set to this function, which should set fSalarm
- and then either longjmp or schedule another SIGALRM. fSalarm is
- never referred to outside of this file, but we don't make it static
- to try to fool compilers which don't understand volatile. */
-
- volatile sig_atomic_t fSalarm;
-
- static RETSIGTYPE
- usalarm (isig)
- int isig;
- {
- #if ! HAVE_SIGACTION && ! HAVE_SIGVEC && ! HAVE_SIGSET
- (void) signal (isig, usalarm);
- #endif
-
- fSalarm = TRUE;
-
- #if HAVE_RESTARTABLE_SYSCALLS
- longjmp (sSjmp_buf, 1);
- #else
- alarm (1);
- #endif
- }
-
- /* We need a simple routine to block SIGINT, SIGQUIT, SIGTERM and
- SIGPIPE and another to restore the original state. When these
- functions are called (in fsysdep_modem_close) SIGHUP is being
- ignored. The routines are isblocksigs, which returns a value of
- type HELD_SIG_MASK and usunblocksigs which takes a single argument
- of type HELD_SIG_MASK. */
-
- #if HAVE_SIGPROCMASK
-
- /* Use the POSIX sigprocmask call. */
-
- #define HELD_SIG_MASK sigset_t
-
- static sigset_t isblocksigs P((void));
-
- static sigset_t
- isblocksigs ()
- {
- sigset_t sblock, sold;
-
- /* These expressions need an extra set of parentheses to avoid a bug
- in SCO 3.2.2. */
- (void) (sigemptyset (&sblock));
- (void) (sigaddset (&sblock, SIGINT));
- (void) (sigaddset (&sblock, SIGQUIT));
- (void) (sigaddset (&sblock, SIGTERM));
- (void) (sigaddset (&sblock, SIGPIPE));
-
- (void) sigprocmask (SIG_BLOCK, &sblock, &sold);
- return sold;
- }
-
- #define usunblocksigs(s) \
- ((void) sigprocmask (SIG_SETMASK, &(s), (sigset_t *) NULL))
-
- #else /* ! HAVE_SIGPROCMASK */
- #if HAVE_SIGBLOCK
-
- /* Use the BSD sigblock and sigsetmask calls. */
-
- #define HELD_SIG_MASK int
-
- #ifndef sigmask
- #define sigmask(i) (1 << ((i) - 1))
- #endif
-
- #define isblocksigs() \
- sigblock (sigmask (SIGINT) | sigmask (SIGQUIT) \
- | sigmask (SIGTERM) | sigmask (SIGPIPE))
-
- #define usunblocksigs(i) ((void) sigsetmask (i))
-
- #else /* ! HAVE_SIGBLOCK */
-
- #if HAVE_SIGHOLD
-
- /* Use the SVR3 sighold and sigrelse calls. */
-
- #define HELD_SIG_MASK int
-
- static int isblocksigs P((void));
-
- static int
- isblocksigs ()
- {
- sighold (SIGINT);
- sighold (SIGQUIT);
- sighold (SIGTERM);
- sighold (SIGPIPE);
- return 0;
- }
-
- static void usunblocksigs P((int));
-
- /*ARGSUSED*/
- static void
- usunblocksigs (i)
- int i;
- {
- sigrelse (SIGINT);
- sigrelse (SIGQUIT);
- sigrelse (SIGTERM);
- sigrelse (SIGPIPE);
- }
-
- #else /* ! HAVE_SIGHOLD */
-
- /* We have no way to block signals. This system will suffer from a
- race condition in fsysdep_modem_close. */
-
- #define HELD_SIG_MASK int
-
- #define isblocksigs() 0
-
- #define usunblocksigs(i)
-
- #endif /* ! HAVE_SIGHOLD */
- #endif /* ! HAVE_SIGBLOCK */
- #endif /* ! HAVE_SIGPROCMASK */
-
- /* Initialize a connection for use on a serial port. */
-
- static boolean
- fsserial_init (qconn, qcmds, zdevice)
- struct sconnection *qconn;
- const struct sconncmds *qcmds;
- const char *zdevice;
- {
- struct ssysdep_conn *q;
-
- q = (struct ssysdep_conn *) xmalloc (sizeof (struct ssysdep_conn));
- if (zdevice == NULL
- && qconn->qport != NULL
- && qconn->qport->uuconf_ttype != UUCONF_PORTTYPE_STDIN)
- zdevice = qconn->qport->uuconf_zname;
- if (zdevice == NULL)
- q->zdevice = NULL;
- else if (*zdevice == '/')
- q->zdevice = zbufcpy (zdevice);
- else
- {
- size_t clen;
-
- clen = strlen (zdevice);
- q->zdevice = zbufalc (sizeof "/dev/" + clen);
- memcpy (q->zdevice, "/dev/", sizeof "/dev/" - 1);
- memcpy (q->zdevice + sizeof "/dev/" - 1, zdevice, clen);
- q->zdevice[sizeof "/dev/" + clen - 1] = '\0';
- }
- q->o = -1;
- q->ord = -1;
- q->owr = -1;
- q->ftli = FALSE;
- qconn->psysdep = (pointer) q;
- qconn->qcmds = qcmds;
- return TRUE;
- }
-
- /* Initialize a connection for use on standard input. */
-
- boolean
- fsysdep_stdin_init (qconn)
- struct sconnection *qconn;
- {
- /* chmod /dev/tty to prevent other users from writing messages to
- it. This is essentially `mesg n'. */
- (void) chmod ("/dev/tty", S_IRUSR | S_IWUSR);
- return fsserial_init (qconn, &sstdincmds, (const char *) NULL);
- }
-
- /* Initialize a connection for use on a modem port. */
-
- boolean
- fsysdep_modem_init (qconn)
- struct sconnection *qconn;
- {
- return fsserial_init (qconn, &smodemcmds,
- qconn->qport->uuconf_u.uuconf_smodem.uuconf_zdevice);
- }
-
- /* Initialize a connection for use on a direct port. */
-
- boolean
- fsysdep_direct_init (qconn)
- struct sconnection *qconn;
- {
- return fsserial_init (qconn, &sdirectcmds,
- qconn->qport->uuconf_u.uuconf_sdirect.uuconf_zdevice);
- }
-
- /* Free up a serial port. */
-
- static void
- usserial_free (qconn)
- struct sconnection *qconn;
- {
- struct ssysdep_conn *qsysdep;
-
- qsysdep = (struct ssysdep_conn *) qconn->psysdep;
- ubuffree (qsysdep->zdevice);
- xfree ((pointer) qsysdep);
- qconn->psysdep = NULL;
- }
-
- #if HAVE_SEQUENT_LOCKFILES
- #define LCK_TEMPLATE "LCK..tty"
- #else
- #define LCK_TEMPLATE "LCK.."
- #endif
-
- /* This routine is used for both locking and unlocking. It is the
- only routine which knows how to translate a device name into the
- name of a lock file. If it can't figure out a name, it does
- nothing and returns TRUE. */
-
- static boolean
- fsserial_lockfile (flok, qconn)
- boolean flok;
- const struct sconnection *qconn;
- {
- struct ssysdep_conn *qsysdep;
- const char *z;
- char *zalc;
- boolean fret;
-
- qsysdep = (struct ssysdep_conn *) qconn->psysdep;
- if (qconn->qport == NULL)
- z = NULL;
- else
- z = qconn->qport->uuconf_zlockname;
- zalc = NULL;
- if (z == NULL)
- {
- #if HAVE_QNX_LOCKFILES
- {
- nid_t idevice_nid;
- char abdevice_nid[13]; /* length of long, a period, and a NUL */
- size_t cdevice_nid;
- const char *zbase;
- size_t clen;
-
- /* If the node ID is explicitly specified as part of the
- pathname to the device, use that. Otherwise, presume the
- device is local to the current node. */
- if (qsysdep->zdevice[0] == '/' && qsysdep->zdevice[1] == '/')
- idevice_nid = (nid_t) strtol (qsysdep->zdevice + 2,
- (char **) NULL, 10);
- else
- idevice_nid = getnid ();
-
- sprintf (abdevice_nid, "%ld.", (long) idevice_nid);
- cdevice_nid = strlen (abdevice_nid);
-
- zbase = strrchr (qsysdep->zdevice, '/') + 1;
- clen = strlen (zbase);
-
- zalc = zbufalc (sizeof LCK_TEMPLATE + cdevice_nid + clen);
-
- memcpy (zalc, LCK_TEMPLATE, sizeof LCK_TEMPLATE - 1);
- memcpy (zalc + sizeof LCK_TEMPLATE - 1, abdevice_nid, cdevice_nid);
- memcpy (zalc + sizeof LCK_TEMPLATE - 1 + cdevice_nid,
- zbase, clen + 1);
-
- z = zalc;
- }
- #else /* ! HAVE_QNX_LOCKFILES */
- #if ! HAVE_SVR4_LOCKFILES
- {
- const char *zbase;
- size_t clen;
-
- zbase = strrchr (qsysdep->zdevice, '/') + 1;
- clen = strlen (zbase);
- zalc = zbufalc (sizeof LCK_TEMPLATE + clen);
- memcpy (zalc, LCK_TEMPLATE, sizeof LCK_TEMPLATE - 1);
- memcpy (zalc + sizeof LCK_TEMPLATE - 1, zbase, clen + 1);
- #if HAVE_SCO_LOCKFILES
- {
- char *zl;
-
- for (zl = zalc + sizeof LCK_TEMPLATE - 1; *zl != '\0'; zl++)
- if (isupper (*zl))
- *zl = tolower (*zl);
- }
- #endif
- z = zalc;
- }
- #else /* HAVE_SVR4_LOCKFILES */
- {
- struct stat s;
-
- if (stat (qsysdep->zdevice, &s) != 0)
- {
- ulog (LOG_ERROR, "stat (%s): %s", qsysdep->zdevice,
- strerror (errno));
- return FALSE;
- }
- zalc = zbufalc (sizeof "LK.1234567890.1234567890.1234567890");
- sprintf (zalc, "LK.%03d.%03d.%03d", major (s.st_dev),
- major (s.st_rdev), minor (s.st_rdev));
- z = zalc;
- }
- #endif /* HAVE_SVR4_LOCKFILES */
- #endif /* ! HAVE_QNX_LOCKFILES */
- }
-
- if (flok)
- fret = fsdo_lock (z, FALSE, (boolean *) NULL);
- else
- fret = fsdo_unlock (z, FALSE);
-
- #if HAVE_COHERENT_LOCKFILES
- if (fret)
- {
- if (flok)
- {
- if (lockttyexist (z + sizeof LCK_TEMPLATE - 1))
- {
- ulog (LOG_NORMAL, "%s: port already locked",
- z + sizeof LCK_TEMPLATE - 1);
- fret = FALSE;
- }
- else
- fret = fscoherent_disable_tty (z + sizeof LCK_TEMPLATE - 1,
- &qsysdep->zenable);
- }
- else
- {
- fret = TRUE;
- if (qsysdep->zenable != NULL)
- {
- const char *azargs[3];
- int aidescs[3];
- pid_t ipid;
-
- azargs[0] = "/etc/enable";
- azargs[1] = qsysdep->zenable;
- azargs[2] = NULL;
- aidescs[0] = SPAWN_NULL;
- aidescs[1] = SPAWN_NULL;
- aidescs[2] = SPAWN_NULL;
-
- ipid = ixsspawn (azargs, aidescs, TRUE, FALSE,
- (const char *) NULL, TRUE, TRUE,
- (const char *) NULL, (const char *) NULL,
- (const char *) NULL);
- if (ipid < 0)
- {
- ulog (LOG_ERROR, "ixsspawn (/etc/enable %s): %s",
- qsysdep->zenable, strerror (errno));
- fret = FALSE;
- }
- else
- {
- if (ixswait ((unsigned long) ipid, (const char *) NULL)
- == 0)
- fret = TRUE;
- else
- fret = FALSE;
- }
- ubuffree (qsysdep->zenable);
- qsysdep->zenable = NULL;
- }
- }
- }
- #endif /* HAVE_COHERENT_LOCKFILES */
-
- ubuffree (zalc);
- return fret;
- }
-
- /* If we can mark a modem line in use, then when we lock a port we
- must open it and mark it in use. We can't wait until the actual
- open because we can't fail out if it is locked then. */
-
- static boolean
- fsserial_lock (qconn, fin)
- struct sconnection *qconn;
- boolean fin;
- {
- if (! fsserial_lockfile (TRUE, qconn))
- return FALSE;
-
- #if HAVE_TIOCSINUSE || HAVE_TIOCEXCL || HAVE_DEV_INFO
- /* Open the line and try to mark it in use. */
- {
- struct ssysdep_conn *qsysdep;
- int iflag;
-
- qsysdep = (struct ssysdep_conn *) qconn->psysdep;
-
- if (fin)
- iflag = 0;
- else
- iflag = iSunblock;
-
- qsysdep->o = open (qsysdep->zdevice, O_RDWR | iflag);
- if (qsysdep->o < 0)
- {
- #if O_NONBLOCK != 0
- if (! fin && iSunblock != O_NONBLOCK && errno == EINVAL)
- {
- iSunblock = O_NONBLOCK;
- qsysdep->o = open (qsysdep->zdevice,
- O_RDWR | O_NONBLOCK);
- }
- #endif
- if (qsysdep->o < 0)
- {
- if (errno != EBUSY)
- ulog (LOG_ERROR, "open (%s): %s", qsysdep->zdevice,
- strerror (errno));
- (void) fsserial_lockfile (FALSE, qconn);
- return FALSE;
- }
- }
-
- #if HAVE_TIOCSINUSE
- /* If we can't mark it in use, return FALSE to indicate that the
- lock failed. */
- if (ioctl (qsysdep->o, TIOCSINUSE, 0) < 0)
- {
- if (errno != EALREADY)
- ulog (LOG_ERROR, "ioctl (TIOCSINUSE): %s", strerror (errno));
- #ifdef TIOCNOTTY
- (void) ioctl (qsysdep->o, TIOCNOTTY, (char *) NULL);
- #endif
- (void) close (qsysdep->o);
- qsysdep->o = -1;
- (void) fsserial_lockfile (FALSE, qconn);
- return FALSE;
- }
- #endif
-
- #if HAVE_DEV_INFO
- /* QNX programs "lock" a serial port by simply opening it and
- checking if some other program also has the port open. If the
- count of openers is greater than one, the program presumes the
- port is "locked" and backs off. This isn't really "locking" of
- course, but it pretty much seems to work. This can result in
- dropping incoming connections if an outgoing connection is
- started at exactly the same time. It would probably be better
- to stop using the lock files at all for this case, but that
- would involve more complex changes to the code, and I'm afraid
- I would break something. -- Joe Wells <jbw@cs.bu.edu> */
- {
- struct _dev_info_entry sdevinfo;
-
- if (dev_info (qsysdep->o, &sdevinfo) == -1)
- {
- ulog (LOG_ERROR, "dev_info: %s", strerror (errno));
- sdevinfo.open_count = 2; /* force presumption of "locked" */
- }
- if (sdevinfo.open_count != 1)
- {
- #ifdef TIOCNOTTY
- (void) ioctl (qsysdep->o, TIOCNOTTY, (char *) NULL);
- #endif /* TIOCNOTTY */
- (void) close (qsysdep->o);
- qsysdep->o = -1;
- (void) fsserial_lockfile (FALSE, qconn);
- return FALSE;
- }
- }
- #endif /* HAVE_DEV_INFO */
-
- if (fcntl (qsysdep->o, F_SETFD,
- fcntl (qsysdep->o, F_GETFD, 0) | FD_CLOEXEC) < 0)
- {
- ulog (LOG_ERROR, "fcntl (FD_CLOEXEC): %s", strerror (errno));
- #ifdef TIOCNOTTY
- (void) ioctl (qsysdep->o, TIOCNOTTY, (char *) NULL);
- #endif
- (void) close (qsysdep->o);
- qsysdep->o = -1;
- (void) fsserial_lockfile (FALSE, qconn);
- return FALSE;
- }
- }
- #endif /* HAVE_TIOCSINUSE || HAVE_TIOCEXCL */
-
- return TRUE;
- }
-
- /* Unlock a modem or direct port. */
-
- static boolean
- fsserial_unlock (qconn)
- struct sconnection *qconn;
- {
- boolean fret;
- struct ssysdep_conn *qsysdep;
-
- fret = TRUE;
-
- /* The file may have been opened by fsserial_lock, so close it here
- if necessary. */
- qsysdep = (struct ssysdep_conn *) qconn->psysdep;
- if (qsysdep->o >= 0)
- {
- #ifdef TIOCNOTTY
- (void) ioctl (qsysdep->o, TIOCNOTTY, (char *) NULL);
- #endif
- if (close (qsysdep->o) < 0)
- {
- ulog (LOG_ERROR, "close: %s", strerror (errno));
- fret = FALSE;
- }
- qsysdep->o = -1;
- }
-
- if (! fsserial_lockfile (FALSE, qconn))
- fret = FALSE;
-
- return fret;
- }
-
- /* A table to map baud rates into index numbers. */
-
- #if HAVE_POSIX_TERMIOS
- typedef speed_t baud_code;
- #else
- typedef int baud_code;
- #endif
-
- static struct sbaud_table
- {
- baud_code icode;
- long ibaud;
- } asSbaud_table[] =
- {
- { B50, 50 },
- { B75, 75 },
- { B110, 110 },
- { B134, 134 },
- { B150, 150 },
- { B200, 200 },
- { B300, 300 },
- { B600, 600 },
- { B1200, 1200 },
- { B1800, 1800 },
- { B2400, 2400 },
- { B4800, 4800 },
- { B9600, 9600 },
- #ifdef B19200
- { B19200, 19200 },
- #else /* ! defined (B19200) */
- #ifdef EXTA
- { EXTA, 19200 },
- #endif /* EXTA */
- #endif /* ! defined (B19200) */
- #ifdef B38400
- { B38400, 38400 },
- #else /* ! defined (B38400) */
- #ifdef EXTB
- { EXTB, 38400 },
- #endif /* EXTB */
- #endif /* ! defined (B38400) */
- #ifdef B57600
- { B57600, 57600 },
- #endif
- #ifdef B76800
- { B76800, 76800 },
- #endif
- #ifdef B115200
- { B115200, 115200 },
- #endif
- { B0, 0 }
- };
-
- #define CBAUD_TABLE (sizeof asSbaud_table / sizeof asSbaud_table[0])
-
- #if HAVE_SYSV_TERMIO || HAVE_POSIX_TERMIOS
- /* Hold the MIN value for the terminal to avoid setting it
- unnecessarily. */
- static int cSmin;
- #endif /* HAVE_SYSV_TERMIO || HAVE_POSIX_TERMIOS */
-
- /* Open a serial line. This sets the terminal settings. We begin in
- seven bit mode and let the protocol change if necessary. If fwait
- is FALSE we open the terminal in non-blocking mode. If flocal is
- TRUE we set CLOCAL on the terminal when using termio[s]; this is
- supposedly required on some versions of BSD/386. */
-
- static boolean
- fsserial_open (qconn, ibaud, fwait, tlocal)
- struct sconnection *qconn;
- long ibaud;
- boolean fwait;
- enum tclocal_setting tlocal;
- {
- struct ssysdep_conn *q;
- baud_code ib;
-
- q = (struct ssysdep_conn *) qconn->psysdep;
-
- if (q->zdevice != NULL)
- {
- #if LOG_DEVICE_PREFIX
- ulog_device (q->zdevice);
- #else
- const char *z;
-
- if (strncmp (q->zdevice, "/dev/", sizeof "/dev/" - 1) == 0)
- z = q->zdevice + sizeof "/dev/" - 1;
- else
- z = q->zdevice;
- ulog_device (z);
- #endif
- }
- else
- {
- const char *zport;
- boolean fdummy;
-
- #if DEBUG > 0
- if (qconn->qport != NULL &&
- qconn->qport->uuconf_ttype != UUCONF_PORTTYPE_STDIN)
- ulog (LOG_FATAL, "fsserial_open: Can't happen");
- #endif
- zport = zsysdep_port_name (&fdummy);
- if (zport != NULL)
- ulog_device (zport);
- }
-
- ib = B0;
- if (ibaud != 0)
- {
- int i;
-
- for (i = 0; i < CBAUD_TABLE; i++)
- if (asSbaud_table[i].ibaud == ibaud)
- break;
- if (i >= CBAUD_TABLE)
- {
- ulog (LOG_ERROR, "Unsupported baud rate %ld", ibaud);
- return FALSE;
- }
- ib = asSbaud_table[i].icode;
- }
-
- /* The port may have already been opened by the locking routine. */
- if (q->o < 0)
- {
- int iflag;
-
- if (fwait)
- iflag = 0;
- else
- iflag = iSunblock;
-
- q->o = open (q->zdevice, O_RDWR | iflag);
- if (q->o < 0)
- {
- #if O_NONBLOCK != 0
- if (! fwait && iSunblock != O_NONBLOCK && errno == EINVAL)
- {
- iSunblock = O_NONBLOCK;
- q->o = open (q->zdevice, O_RDWR | O_NONBLOCK);
- }
- #endif
- if (q->o < 0)
- {
- ulog (LOG_ERROR, "open (%s): %s", q->zdevice,
- strerror (errno));
- return FALSE;
- }
- }
-
- if (fcntl (q->o, F_SETFD, fcntl (q->o, F_GETFD, 0) | FD_CLOEXEC) < 0)
- {
- ulog (LOG_ERROR, "fcntl (FD_CLOEXEC): %s", strerror (errno));
- return FALSE;
- }
- }
-
- /* Get the port flags, and make sure the ports are blocking. */
-
- q->iflags = fcntl (q->o, F_GETFL, 0);
- if (q->iflags < 0)
- {
- ulog (LOG_ERROR, "fcntl: %s", strerror (errno));
- return FALSE;
- }
- q->iwr_flags = -1;
-
- if (! fgetterminfo (q->o, &q->sorig))
- {
- q->fterminal = FALSE;
- return TRUE;
- }
-
- q->fterminal = TRUE;
-
- q->snew = q->sorig;
-
- #if HAVE_BSD_TTY
-
- q->snew.stty.sg_flags = RAW | ANYP;
- if (ibaud == 0)
- ib = q->snew.stty.sg_ospeed;
- else
- {
- q->snew.stty.sg_ispeed = ib;
- q->snew.stty.sg_ospeed = ib;
- }
-
- /* We don't want to receive any interrupt characters. */
- q->snew.stchars.t_intrc = -1;
- q->snew.stchars.t_quitc = -1;
- q->snew.stchars.t_eofc = -1;
- q->snew.stchars.t_brkc = -1;
- q->snew.sltchars.t_suspc = -1;
- q->snew.sltchars.t_rprntc = -1;
- q->snew.sltchars.t_dsuspc = -1;
- q->snew.sltchars.t_flushc = -1;
- q->snew.sltchars.t_werasc = -1;
- q->snew.sltchars.t_lnextc = -1;
-
- #ifdef NTTYDISC
- /* We want to use the ``new'' terminal driver so that we can use the
- local mode bits to control XON/XOFF. */
- {
- int iparam;
-
- if (ioctl (q->o, TIOCGETD, &iparam) >= 0
- && iparam != NTTYDISC)
- {
- iparam = NTTYDISC;
- (void) ioctl (q->o, TIOCSETD, &iparam);
- }
- }
- #endif
-
- #ifdef TIOCHPCL
- /* When the file is closed, hang up the line. This is a safety
- measure in case the program crashes. */
- (void) ioctl (q->o, TIOCHPCL, 0);
- #endif
-
- #ifdef TIOCFLUSH
- {
- int iparam;
-
- /* Flush pending input. */
- #ifdef FREAD
- iparam = FREAD;
- #else
- iparam = 0;
- #endif
- (void) ioctl (q->o, TIOCFLUSH, &iparam);
- }
- #endif /* TIOCFLUSH */
-
- #endif /* HAVE_BSD_TTY */
-
- #if HAVE_SYSV_TERMIO
-
- if (ibaud == 0)
- ib = q->snew.c_cflag & CBAUD;
-
- q->snew.c_iflag &=~ ICLEAR_IFLAG;
- q->snew.c_oflag &=~ ICLEAR_OFLAG;
- q->snew.c_cflag &=~ ICLEAR_CFLAG;
- q->snew.c_cflag |= ib | ISET_CFLAG;
- q->snew.c_lflag &=~ ICLEAR_LFLAG;
- cSmin = 1;
- q->snew.c_cc[VMIN] = cSmin;
- q->snew.c_cc[VTIME] = 1;
-
- #ifdef TCFLSH
- /* Flush pending input. */
- (void) ioctl (q->o, TCFLSH, 0);
- #endif
-
- #endif /* HAVE_SYSV_TERMIO */
-
- #if HAVE_POSIX_TERMIOS
-
- if (ibaud == 0)
- ib = cfgetospeed (&q->snew);
-
- q->snew.c_iflag &=~ ICLEAR_IFLAG;
- q->snew.c_oflag &=~ ICLEAR_OFLAG;
- q->snew.c_cflag &=~ ICLEAR_CFLAG;
- q->snew.c_cflag |= ISET_CFLAG;
- q->snew.c_lflag &=~ ICLEAR_LFLAG;
- cSmin = 1;
- q->snew.c_cc[VMIN] = cSmin;
- q->snew.c_cc[VTIME] = 1;
-
- (void) cfsetospeed (&q->snew, ib);
- (void) cfsetispeed (&q->snew, ib);
-
- /* Flush pending input. */
- (void) tcflush (q->o, TCIFLUSH);
-
- #endif /* HAVE_POSIX_TERMIOS */
-
- #if HAVE_SYSV_TERMIO || HAVE_POSIX_TERMIOS
- switch (tlocal)
- {
- case SET_CLOCAL:
- q->snew.c_cflag |= CLOCAL;
- break;
- case CLEAR_CLOCAL:
- q->snew.c_cflag &=~ CLOCAL;
- break;
- case IGNORE_CLOCAL:
- break;
- }
- #endif /* HAVE_SYSV_TERMIO || HAVE_POSIX_TERMIOS */
-
- if (! fsetterminfo (q->o, &q->snew))
- {
- ulog (LOG_ERROR, "Can't set terminal settings: %s", strerror (errno));
- return FALSE;
- }
-
- #ifdef TIOCSCTTY
- /* On BSD 4.4, make it our controlling terminal. */
- (void) ioctl (q->o, TIOCSCTTY, 0);
- #endif
-
- if (ibaud != 0)
- q->ibaud = ibaud;
- else
- {
- int i;
-
- q->ibaud = (long) 1200;
- for (i = 0; i < CBAUD_TABLE; i++)
- {
- if (asSbaud_table[i].icode == ib)
- {
- q->ibaud = asSbaud_table[i].ibaud;
- break;
- }
- }
-
- DEBUG_MESSAGE1 (DEBUG_PORT,
- "fsserial_open: Baud rate is %ld", q->ibaud);
- }
-
- return TRUE;
- }
-
- /* Open a standard input port. The code alternates q->o between
- q->ord and q->owr as appropriate. It is always q->ord before any
- call to fsblock. */
-
- static boolean
- fsstdin_open (qconn, ibaud, fwait)
- struct sconnection *qconn;
- long ibaud;
- boolean fwait;
- {
- struct ssysdep_conn *q;
-
- q = (struct ssysdep_conn *) qconn->psysdep;
- q->ord = 0;
- q->owr = 1;
-
- q->o = q->ord;
- if (! fsserial_open (qconn, ibaud, fwait, IGNORE_CLOCAL))
- return FALSE;
- q->iwr_flags = fcntl (q->owr, F_GETFL, 0);
- if (q->iwr_flags < 0)
- {
- ulog (LOG_ERROR, "fcntl: %s", strerror (errno));
- return FALSE;
- }
- return TRUE;
- }
-
- /* Open a modem port. */
-
- static boolean
- fsmodem_open (qconn, ibaud, fwait)
- struct sconnection *qconn;
- long ibaud;
- boolean fwait;
- {
- struct uuconf_modem_port *qm;
-
- qm = &qconn->qport->uuconf_u.uuconf_smodem;
- if (ibaud == (long) 0)
- ibaud = qm->uuconf_ibaud;
-
- if (! fsserial_open (qconn, ibaud, fwait,
- fwait ? CLEAR_CLOCAL : SET_CLOCAL))
- return FALSE;
-
- /* If we are waiting for carrier, then turn on hardware flow
- control. We don't turn on hardware flow control when dialing
- out, because some modems don't assert the necessary signals until
- they see carrier. Instead, we turn on hardware flow control in
- fsmodem_carrier. */
- if (fwait
- && ! fsserial_hardflow (qconn, qm->uuconf_fhardflow))
- return FALSE;
-
- return TRUE;
- }
-
- /* Open a direct port. */
-
- static boolean
- fsdirect_open (qconn, ibaud, fwait)
- struct sconnection *qconn;
- long ibaud;
- boolean fwait;
- {
- struct uuconf_direct_port *qd;
-
- qd = &qconn->qport->uuconf_u.uuconf_sdirect;
- if (ibaud == (long) 0)
- ibaud = qd->uuconf_ibaud;
- if (! fsserial_open (qconn, ibaud, fwait,
- qd->uuconf_fcarrier ? CLEAR_CLOCAL : SET_CLOCAL))
- return FALSE;
-
- /* Always turn on hardware flow control for a direct port when it is
- opened. There is no other sensible time to turn it on. */
- return fsserial_hardflow (qconn, qd->uuconf_fhardflow);
- }
-
- /* Change the blocking status of the port. We keep track of the
- current blocking status to avoid calling fcntl unnecessarily; fcntl
- turns out to be surprisingly expensive, at least on Ultrix. */
-
- static boolean
- fsblock (qs, fblock)
- struct ssysdep_conn *qs;
- boolean fblock;
- {
- int iwant;
- int isys;
-
- if (fblock)
- iwant = qs->iflags &~ (O_NDELAY | O_NONBLOCK);
- else
- iwant = qs->iflags | iSunblock;
-
- if (iwant == qs->iflags)
- return TRUE;
-
- isys = fcntl (qs->o, F_SETFL, iwant);
- if (isys < 0)
- {
- #if O_NONBLOCK != 0
- if (! fblock && iSunblock != O_NONBLOCK && errno == EINVAL)
- {
- iSunblock = O_NONBLOCK;
- iwant = qs->iflags | O_NONBLOCK;
- isys = fcntl (qs->o, F_SETFL, iwant);
- }
- #endif
- if (isys < 0)
- {
- ulog (LOG_ERROR, "fcntl: %s", strerror (errno));
- return FALSE;
- }
- }
-
- qs->iflags = iwant;
-
- if (qs->iwr_flags >= 0 && qs->ord != qs->owr)
- {
- if (fblock)
- iwant = qs->iwr_flags &~ (O_NDELAY | O_NONBLOCK);
- else
- iwant = qs->iwr_flags | iSunblock;
-
- if (fcntl (qs->owr, F_SETFL, iwant) < 0)
- {
- /* We don't bother to fix up iSunblock here, since we
- succeeded above. */
- ulog (LOG_ERROR, "fcntl: %s", strerror (errno));
- return FALSE;
- }
-
- qs->iwr_flags = iwant;
- }
-
- return TRUE;
- }
-
- /* Close a serial port. */
-
- static boolean
- fsserial_close (q)
- struct ssysdep_conn *q;
- {
- if (q->o >= 0)
- {
- /* Use a 30 second timeout to avoid hanging while draining
- output. */
- if (q->fterminal)
- {
- fSalarm = FALSE;
-
- if (fsysdep_catch ())
- {
- usysdep_start_catch ();
- usset_signal (SIGALRM, usalarm, TRUE, (boolean *) NULL);
- (void) alarm (30);
-
- (void) fsetterminfodrain (q->o, &q->sorig);
- }
-
- usset_signal (SIGALRM, SIG_IGN, TRUE, (boolean *) NULL);
- (void) alarm (0);
- usysdep_end_catch ();
-
- /* If we timed out, use the non draining call. Hopefully
- this can't hang. */
- if (fSalarm)
- (void) fsetterminfo (q->o, &q->sorig);
- }
-
- #ifdef TIOCNOTTY
- /* We don't want this as our controlling terminal any more, so
- get rid of it. This is necessary because we don't want to
- open /dev/tty, since that can confuse the serial port locking
- on some computers. */
- (void) ioctl (q->o, TIOCNOTTY, (char *) NULL);
- #endif
-
- (void) close (q->o);
- q->o = -1;
-
- /* Sleep to give the terminal a chance to settle, in case we are
- about to call out again. */
- sleep (2);
- }
-
- return TRUE;
- }
-
- /* Close a stdin port. */
-
- /*ARGSUSED*/
- static boolean
- fsstdin_close (qconn, puuconf, qdialer, fsuccess)
- struct sconnection *qconn;
- pointer puuconf;
- struct uuconf_dialer *qdialer;
- boolean fsuccess;
- {
- struct ssysdep_conn *qsysdep;
-
- qsysdep = (struct ssysdep_conn *) qconn->psysdep;
- (void) close (qsysdep->owr);
- (void) close (2);
- qsysdep->o = qsysdep->ord;
- return fsserial_close (qsysdep);
- }
-
- /* Close a modem port. */
-
- static boolean
- fsmodem_close (qconn, puuconf, qdialer, fsuccess)
- struct sconnection *qconn;
- pointer puuconf;
- struct uuconf_dialer *qdialer;
- boolean fsuccess;
- {
- struct ssysdep_conn *qsysdep;
- boolean fret;
- struct uuconf_dialer sdialer;
- const struct uuconf_chat *qchat;
-
- qsysdep = (struct ssysdep_conn *) qconn->psysdep;
-
- fret = TRUE;
-
- /* Figure out the dialer so that we can run the complete or abort
- chat scripts. */
- if (qdialer == NULL)
- {
- if (qconn->qport->uuconf_u.uuconf_smodem.uuconf_pzdialer != NULL)
- {
- const char *zdialer;
- int iuuconf;
-
- zdialer = qconn->qport->uuconf_u.uuconf_smodem.uuconf_pzdialer[0];
- iuuconf = uuconf_dialer_info (puuconf, zdialer, &sdialer);
- if (iuuconf == UUCONF_SUCCESS)
- qdialer = &sdialer;
- else
- {
- ulog_uuconf (LOG_ERROR, puuconf, iuuconf);
- fret = FALSE;
- }
- }
- else
- qdialer = qconn->qport->uuconf_u.uuconf_smodem.uuconf_qdialer;
- }
-
- /* Get the complete or abort chat script to use. */
- qchat = NULL;
- if (qdialer != NULL)
- {
- if (fsuccess)
- qchat = &qdialer->uuconf_scomplete;
- else
- qchat = &qdialer->uuconf_sabort;
- }
-
- if (qchat != NULL
- && (qchat->uuconf_pzprogram != NULL
- || qchat->uuconf_pzchat != NULL))
- {
- boolean fsighup_ignored;
- HELD_SIG_MASK smask;
- int i;
- sig_atomic_t afhold[INDEXSIG_COUNT];
-
- /* We're no longer interested in carrier. */
- (void) fsmodem_carrier (qconn, FALSE);
-
- /* The port I/O routines check whether any signal has been
- received, and abort if one has. While we are closing down
- the modem, we don't care if we received a signal in the past,
- but we do care if we receive a new signal (otherwise it would
- be difficult to kill a uucico which was closing down a
- modem). We never care if we get SIGHUP at this point. So we
- turn off SIGHUP, remember what signals we've already seen,
- and clear our notion of what signals we've seen. We have to
- block the signals while we remember and clear the array,
- since we might otherwise miss a signal which occurred between
- the copy and the clear (old systems can't block signals; they
- will just have to suffer the race). */
- usset_signal (SIGHUP, SIG_IGN, FALSE, &fsighup_ignored);
- smask = isblocksigs ();
- for (i = 0; i < INDEXSIG_COUNT; i++)
- {
- afhold[i] = afSignal[i];
- afSignal[i] = FALSE;
- }
- usunblocksigs (smask);
-
- if (! fchat (qconn, puuconf, qchat, (const struct uuconf_system *) NULL,
- (const struct uuconf_dialer *) NULL, (const char *) NULL,
- FALSE, qconn->qport->uuconf_zname,
- qsysdep->ibaud))
- fret = FALSE;
-
- /* Restore the old signal array and the SIGHUP handler. It is
- not necessary to block signals here, since all we are doing
- is exactly what the signal handler itself would do if the
- signal occurred. */
- for (i = 0; i < INDEXSIG_COUNT; i++)
- if (afhold[i])
- afSignal[i] = TRUE;
- if (! fsighup_ignored)
- usset_signal (SIGHUP, ussignal, TRUE, (boolean *) NULL);
- }
-
- if (qdialer != NULL
- && qdialer == &sdialer)
- (void) uuconf_dialer_free (puuconf, &sdialer);
-
- #if ! HAVE_RESET_BUG
- /* Reset the terminal to make sure we drop DTR. It should be
- dropped when we close the descriptor, but that doesn't seem to
- happen on some systems. Use a 30 second timeout to avoid hanging
- while draining output. */
- if (qsysdep->fterminal)
- {
- #if HAVE_BSD_TTY
- qsysdep->snew.stty.sg_ispeed = B0;
- qsysdep->snew.stty.sg_ospeed = B0;
- #endif
- #if HAVE_SYSV_TERMIO
- qsysdep->snew.c_cflag = (qsysdep->snew.c_cflag &~ CBAUD) | B0;
- #endif
- #if HAVE_POSIX_TERMIOS
- (void) cfsetospeed (&qsysdep->snew, B0);
- #endif
-
- fSalarm = FALSE;
-
- if (fsysdep_catch ())
- {
- usysdep_start_catch ();
- usset_signal (SIGALRM, usalarm, TRUE, (boolean *) NULL);
- (void) alarm (30);
-
- (void) fsetterminfodrain (qsysdep->o, &qsysdep->snew);
- }
-
- usset_signal (SIGALRM, SIG_IGN, TRUE, (boolean *) NULL);
- (void) alarm (0);
- usysdep_end_catch ();
-
- /* Let the port settle. */
- sleep (2);
- }
- #endif /* ! HAVE_RESET_BUG */
-
- if (! fsserial_close (qsysdep))
- fret = FALSE;
-
- return fret;
- }
-
- /* Close a direct port. */
-
- /*ARGSUSED*/
- static boolean
- fsdirect_close (qconn, puuconf, qdialer, fsuccess)
- struct sconnection *qconn;
- pointer puuconf;
- struct uuconf_dialer *qdialer;
- boolean fsuccess;
- {
- return fsserial_close ((struct ssysdep_conn *) qconn->psysdep);
- }
-
- /* Begin dialing out on a modem port. This opens the dialer device if
- there is one. */
-
- boolean
- fsysdep_modem_begin_dial (qconn, qdial)
- struct sconnection *qconn;
- struct uuconf_dialer *qdial;
- {
- struct ssysdep_conn *qsysdep;
- const char *z;
-
- qsysdep = (struct ssysdep_conn *) qconn->psysdep;
-
- #ifdef TIOCMODEM
- /* If we can tell the modem to obey modem control, do so. */
- {
- int iperm;
-
- iperm = 0;
- (void) ioctl (qsysdep->o, TIOCMODEM, &iperm);
- }
- #endif /* TIOCMODEM */
-
- /* If we supposed to toggle DTR, do so. */
-
- if (qdial->uuconf_fdtr_toggle)
- {
- #ifdef TIOCCDTR
- (void) ioctl (qsysdep->o, TIOCCDTR, 0);
- sleep (2);
- (void) ioctl (qsysdep->o, TIOCSDTR, 0);
- #else /* ! defined (TIOCCDTR) */
- if (qsysdep->fterminal)
- {
- sterminal sbaud;
-
- sbaud = qsysdep->snew;
-
- #if HAVE_BSD_TTY
- sbaud.stty.sg_ispeed = B0;
- sbaud.stty.sg_ospeed = B0;
- #endif
- #if HAVE_SYSV_TERMIO
- sbaud.c_cflag = (sbaud.c_cflag &~ CBAUD) | B0;
- #endif
- #if HAVE_POSIX_TERMIOS
- (void) cfsetospeed (&sbaud, B0);
- #endif
-
- (void) fsetterminfodrain (qsysdep->o, &sbaud);
- sleep (2);
- (void) fsetterminfo (qsysdep->o, &qsysdep->snew);
- }
- #endif /* ! defined (TIOCCDTR) */
-
- if (qdial->uuconf_fdtr_toggle_wait)
- sleep (2);
- }
-
- if (! fsmodem_carrier (qconn, FALSE))
- return FALSE;
-
- /* Open the dial device if there is one. */
- z = qconn->qport->uuconf_u.uuconf_smodem.uuconf_zdial_device;
- if (z != NULL)
- {
- char *zfree;
- int o;
-
- qsysdep->ohold = qsysdep->o;
-
- zfree = NULL;
- if (*z != '/')
- {
- zfree = zbufalc (sizeof "/dev/" + strlen (z));
- sprintf (zfree, "/dev/%s", z);
- z = zfree;
- }
-
- o = open ((char *) z, O_RDWR | O_NOCTTY);
- if (o < 0)
- {
- ulog (LOG_ERROR, "open (%s): %s", z, strerror (errno));
- ubuffree (zfree);
- return FALSE;
- }
- ubuffree (zfree);
-
- if (fcntl (o, F_SETFD, fcntl (o, F_GETFD, 0) | FD_CLOEXEC) < 0)
- {
- ulog (LOG_ERROR, "fcntl (FD_CLOEXEC): %s", strerror (errno));
- (void) close (o);
- return FALSE;
- }
-
- qsysdep->o = o;
- }
-
- return TRUE;
- }
-
- /* Tell the port to require or not require carrier. On BSD this uses
- TIOCCAR and TIOCNCAR, which I assume are generally supported (it
- can also use the LNOMDM bit supported by IS68K Unix). On System V
- it resets or sets CLOCAL. We only require carrier if the port
- supports it. This will only be called with fcarrier TRUE if the
- dialer supports carrier. */
-
- static boolean
- fsmodem_carrier (qconn, fcarrier)
- struct sconnection *qconn;
- boolean fcarrier;
- {
- register struct ssysdep_conn *q;
- struct uuconf_modem_port *qm;
-
- q = (struct ssysdep_conn *) qconn->psysdep;
-
- if (! q->fterminal)
- return TRUE;
-
- qm = &qconn->qport->uuconf_u.uuconf_smodem;
- if (fcarrier)
- {
- if (qm->uuconf_fcarrier)
- {
- #ifdef TIOCCAR
- /* Tell the modem to pay attention to carrier. */
- if (ioctl (q->o, TIOCCAR, 0) < 0)
- {
- ulog (LOG_ERROR, "ioctl (TIOCCAR): %s", strerror (errno));
- return FALSE;
- }
- #endif /* TIOCCAR */
-
- #if HAVE_BSD_TTY
- #ifdef LNOMDM
- /* IS68K Unix uses a local LNOMDM bit. */
- {
- int iparam;
-
- iparam = LNOMDM;
- if (ioctl (q->o, TIOCLBIC, &iparam) < 0)
- {
- ulog (LOG_ERROR, "ioctl (TIOCLBIC, LNOMDM): %s",
- strerror (errno));
- return FALSE;
- }
- }
- #endif /* LNOMDM */
- #endif /* HAVE_BSD_TTY */
-
- #if HAVE_SYSV_TERMIO || HAVE_POSIX_TERMIOS
- /* Put the modem into nonlocal mode. */
- q->snew.c_cflag &=~ CLOCAL;
- if (! fsetterminfo (q->o, &q->snew))
- {
- ulog (LOG_ERROR, "Can't clear CLOCAL: %s", strerror (errno));
- return FALSE;
- }
- #endif /* HAVE_SYSV_TERMIO || HAVE_POSIX_TERMIOS */
- }
-
- /* Turn on hardware flow control after turning on carrier. We
- don't do it until now because some modems don't assert the
- right signals until they see carrier. */
- if (! fsserial_hardflow (qconn, qm->uuconf_fhardflow))
- return FALSE;
- }
- else
- {
- /* Turn off any hardware flow control before turning off
- carrier. */
- if (! fsserial_hardflow (qconn, FALSE))
- return FALSE;
-
- #ifdef TIOCNCAR
- /* Tell the modem to ignore carrier. */
- if (ioctl (q->o, TIOCNCAR, 0) < 0)
- {
- ulog (LOG_ERROR, "ioctl (TIOCNCAR): %s", strerror (errno));
- return FALSE;
- }
- #endif /* TIOCNCAR */
-
- #if HAVE_BSD_TTY
- #ifdef LNOMDM
- /* IS68K Unix uses a local LNOMDM bit. */
- {
- int iparam;
-
- iparam = LNOMDM;
- if (ioctl (q->o, TIOCLBIS, &iparam) < 0)
- {
- ulog (LOG_ERROR, "ioctl (TIOCLBIS, LNOMDM): %s",
- strerror (errno));
- return FALSE;
- }
- }
- #endif /* LNOMDM */
- #endif /* HAVE_BSD_TTY */
-
- #if HAVE_SYSV_TERMIO || HAVE_POSIX_TERMIOS
- /* Put the modem into local mode (ignore carrier) to start the chat
- script. */
- q->snew.c_cflag |= CLOCAL;
- if (! fsetterminfo (q->o, &q->snew))
- {
- ulog (LOG_ERROR, "Can't set CLOCAL: %s", strerror (errno));
- return FALSE;
- }
-
- #if HAVE_CLOCAL_BUG
- /* On SCO and AT&T UNIX PC you have to reopen the port. */
- {
- int onew;
-
- onew = open (q->zdevice, O_RDWR);
- if (onew < 0)
- {
- ulog (LOG_ERROR, "open (%s): %s", q->zdevice, strerror (errno));
- return FALSE;
- }
-
- if (fcntl (onew, F_SETFD,
- fcntl (onew, F_GETFD, 0) | FD_CLOEXEC) < 0)
- {
- ulog (LOG_ERROR, "fcntl (FD_CLOEXEC): %s", strerror (errno));
- (void) close (onew);
- return FALSE;
- }
-
- (void) close (q->o);
- q->o = onew;
- }
- #endif /* HAVE_CLOCAL_BUG */
-
- #endif /* HAVE_SYSV_TERMIO || HAVE_POSIX_TERMIOS */
- }
-
- return TRUE;
- }
-
- /* Tell the port to use hardware flow control. There is no standard
- mechanism for controlling this. This implementation supports
- CRTSCTS on SunOS, RTS/CTSFLOW on 386(ish) unix, CTSCD on the 3b1,
- CCTS_OFLOW/CRTS_IFLOW on BSDI, TXADDCD/TXDELCD on AIX, and IRTS on
- NCR Tower. If you know how to do it on other systems, please
- implement it and send me the patches. */
-
- static boolean
- fsserial_hardflow (qconn, fhardflow)
- struct sconnection *qconn;
- boolean fhardflow;
- {
- register struct ssysdep_conn *q;
-
- q = (struct ssysdep_conn *) qconn->psysdep;
-
- if (! q->fterminal)
- return TRUE;
-
- /* Don't do anything if we don't know what to do. */
- #if HAVE_BSD_TTY
- #define HAVE_HARDFLOW 0
- #endif
- #if HAVE_SYSV_TERMIO || HAVE_POSIX_TERMIOS
- #if ! HAVE_TXADDCD
- #ifndef CRTSFL
- #ifndef CRTSCTS
- #ifndef CTSCD
- #ifndef CCTS_OFLOW
- #ifndef IRTS
- #define HAVE_HARDFLOW 0
- #endif
- #endif
- #endif
- #endif
- #endif
- #endif
- #endif
-
- #ifndef HAVE_HARDFLOW
- #define HAVE_HARDFLOW 1
- #endif
-
- #if HAVE_HARDFLOW
- if (fhardflow)
- {
- #if HAVE_TXADDCD
- /* The return value does not reliably indicate whether this
- actually succeeded. */
- (void) ioctl (q->o, TXADDCD, "rts");
- #else /* ! HAVE_TXADDCD */
- #if HAVE_SYSV_TERMIO || HAVE_POSIX_TERMIOS
- #ifdef CRTSFL
- q->snew.c_cflag |= CRTSFL;
- q->snew.c_cflag &=~ (RTSFLOW | CTSFLOW);
- #endif /* defined (CRTSFL) */
- #ifdef CRTSCTS
- q->snew.c_cflag |= CRTSCTS;
- #endif /* defined (CRTSCTS) */
- #ifdef CTSCD
- q->snew.c_cflag |= CTSCD;
- #endif /* defined (CTSCD) */
- #ifdef CCTS_OFLOW
- q->snew.c_cflag |= CCTS_OFLOW | CRTS_IFLOW;
- #endif
- #ifdef IRTS
- q->snew.c_iflag |= IRTS;
- #endif
- #endif /* HAVE_SYSV_TERMIO || HAVE_POSIX_TERMIOS */
- if (! fsetterminfo (q->o, &q->snew))
- {
- ulog (LOG_ERROR, "Can't enable hardware flow control: %s",
- strerror (errno));
- return FALSE;
- }
- #endif /* ! HAVE_TXADDCD */
- }
- else
- {
- #if HAVE_TXADDCD
- /* The return value does not reliably indicate whether this
- actually succeeded. */
- (void) ioctl (q->o, TXDELCD, "rts");
- #else /* ! HAVE_TXADDCD */
- #if HAVE_SYSV_TERMIO || HAVE_POSIX_TERMIOS
- #ifdef CRTSFL
- q->snew.c_cflag &=~ CRTSFL;
- q->snew.c_cflag &=~ (RTSFLOW | CTSFLOW);
- #endif /* defined (CRTSFL) */
- #ifdef CRTSCTS
- q->snew.c_cflag &=~ CRTSCTS;
- #endif /* defined (CRTSCTS) */
- #ifdef CTSCD
- q->snew.c_cflag &=~ CTSCD;
- #endif /* defined (CTSCD) */
- #ifdef CCTS_OFLOW
- q->snew.c_cflag &=~ (CCTS_OFLOW | CRTS_IFLOW);
- #endif
- #ifdef IRTS
- q->snew.c_iflag &=~ IRTS;
- #endif
- #endif /* HAVE_SYSV_TERMIO || HAVE_POSIX_TERMIOS */
- if (! fsetterminfo (q->o, &q->snew))
- {
- ulog (LOG_ERROR, "Can't disable hardware flow control: %s",
- strerror (errno));
- return FALSE;
- }
- #endif /* ! HAVE_TXADDCD */
- }
- #endif /* HAVE_HARDFLOW */
-
- return TRUE;
- }
-
- /* Finish dialing out on a modem by closing any dialer device and waiting
- for carrier. */
-
- boolean
- fsysdep_modem_end_dial (qconn, qdial)
- struct sconnection *qconn;
- struct uuconf_dialer *qdial;
- {
- struct ssysdep_conn *q;
-
- q = (struct ssysdep_conn *) qconn->psysdep;
-
- if (qconn->qport->uuconf_u.uuconf_smodem.uuconf_zdial_device != NULL)
- {
- (void) close (q->o);
- q->o = q->ohold;
- }
-
- if (qconn->qport->uuconf_u.uuconf_smodem.uuconf_fcarrier
- && qdial->uuconf_fcarrier)
- {
- /* Tell the port that we need carrier. */
- if (! fsmodem_carrier (qconn, TRUE))
- return FALSE;
-
- #ifdef TIOCWONLINE
-
- /* We know how to wait for carrier, so do so. */
-
- /* If we already got a signal, just quit now. */
- if (FGOT_QUIT_SIGNAL ())
- return FALSE;
-
- /* This bit of code handles signals just like fsysdep_conn_read
- does. See that function for a longer explanation. */
-
- /* Use fsysdep_catch to handle a longjmp from the signal
- handler. */
-
- fSalarm = FALSE;
-
- if (fsysdep_catch ())
- {
- /* Start catching SIGALRM; normally we ignore it. */
- usysdep_start_catch ();
- usset_signal (SIGALRM, usalarm, TRUE, (boolean *) NULL);
- (void) alarm (qdial->uuconf_ccarrier_wait);
-
- /* We really don't care if we get an error, since that will
- probably just mean that TIOCWONLINE isn't supported in
- which case there's nothing we can do anyhow. If we get
- SIGINT we want to keep waiting for carrier, because
- SIGINT just means don't start any new sessions. We don't
- handle SIGINT correctly if we do a longjmp in the signal
- handler; too bad. */
- while (ioctl (q->o, TIOCWONLINE, 0) < 0
- && errno == EINTR)
- {
- /* Log the signal. */
- ulog (LOG_ERROR, (const char *) NULL);
- if (FGOT_QUIT_SIGNAL () || fSalarm)
- break;
- }
- }
-
- /* Turn off the pending SIGALRM and ignore SIGALARM again. */
- usset_signal (SIGALRM, SIG_IGN, TRUE, (boolean *) NULL);
- (void) alarm (0);
- usysdep_end_catch ();
-
- /* If we got a random signal, just return FALSE. */
- if (FGOT_QUIT_SIGNAL ())
- return FALSE;
-
- /* If we timed out, give an error. */
- if (fSalarm)
- {
- ulog (LOG_ERROR, "Timed out waiting for carrier");
- return FALSE;
- }
-
- #else /* ! defined (TIOCWONLINE) */
-
- /* Try to open the port again without using O_NDELAY. In
- principle, the open should delay until carrier is available.
- This may not work on some systems, so we just ignore any
- errors. */
- {
- int onew;
-
- onew = open (q->zdevice, O_RDWR);
- if (onew >= 0)
- {
- boolean fbad;
- int iflags;
-
- fbad = FALSE;
-
- if (fcntl (onew, F_SETFD,
- fcntl (onew, F_GETFD, 0) | FD_CLOEXEC) < 0)
- fbad = TRUE;
-
- if (! fbad)
- {
- iflags = fcntl (onew, F_GETFL, 0);
- if (iflags < 0
- || ! fsetterminfo (onew, &q->snew))
- fbad = TRUE;
- }
-
- if (fbad)
- (void) close (onew);
- else
- {
- (void) close (q->o);
- q->o = onew;
- q->iflags = iflags;
- #if HAVE_TIOCSINUSE
- (void) ioctl (onew, TIOCSINUSE, 0);
- #endif
- }
- }
- }
-
- #endif /* ! defined (TIOCWONLINE) */
- }
-
- return TRUE;
- }
-
- /* Read data from a connection, with a timeout. This routine handles
- all types of connections, including TLI.
-
- This function should return when we have read cmin characters or
- the timeout has occurred. We have to work a bit to get Unix to do
- this efficiently on a terminal. The simple implementation
- schedules a SIGALRM signal and then calls read; if there is a
- single character available, the call to read will return
- immediately, so there must be a loop which terminates when the
- SIGALRM is delivered or the correct number of characters has been
- read. This can be very inefficient with a fast CPU or a low baud
- rate (or both!), since each call to read may return only one or two
- characters.
-
- Under POSIX or System V, we can specify a minimum number of
- characters to read, so there is no serious trouble.
-
- Under BSD, we figure out how many characters we have left to read,
- how long it will take for them to arrive at the current baud rate,
- and sleep that long.
-
- Doing this with a timeout and avoiding all possible race conditions
- get very hairy, though. Basically, we're going to schedule a
- SIGALRM for when the timeout expires. I don't really want to do a
- longjmp in the SIGALRM handler, though, because that may lose data.
- Therefore, I have the signal handler set a variable. However, this
- means that there will be a span of time between the time the code
- checks the variable and the time it calls the read system call; if
- the SIGALRM occurs during that time, the read might hang forever.
- To avoid this, the SIGALRM handler not only sets a global variable,
- it also schedules another SIGALRM for one second in the future
- (POSIX specifies that a signal handler is permitted to safely call
- alarm). To avoid getting a continual sequence of SIGALRM
- interrupts, we change the signal handler to ignore SIGALRM when
- we're about to exit the function. This means that every time we
- execute fsysdep_conn_read we make at least five system calls. It's
- the best I've been able to come up with, though.
-
- When fsysdep_conn_read finishes, there will be no SIGALRM scheduled
- and SIGALRM will be ignored. */
-
- boolean
- fsysdep_conn_read (qconn, zbuf, pclen, cmin, ctimeout, freport)
- struct sconnection *qconn;
- char *zbuf;
- size_t *pclen;
- size_t cmin;
- int ctimeout;
- boolean freport;
- {
- CATCH_PROTECT size_t cwant;
- boolean fret;
- register struct ssysdep_conn * const q
- = (struct ssysdep_conn *) qconn->psysdep;
- int cwouldblock;
-
- cwant = *pclen;
- *pclen = 0;
-
- /* Guard against a bad timeout. We return TRUE when a timeout
- expires. It is possible to get a negative timeout here because
- the calling code does not check user supplied timeouts for
- plausibility. */
- if (ctimeout <= 0)
- return TRUE;
-
- /* We want to do a blocking read. */
- if (! fsblock (q, TRUE))
- return FALSE;
-
- fSalarm = FALSE;
-
- /* We're going to set up an alarm signal to last for the entire
- read. If the read system call cannot be interrupted, the signal
- handler will do a longjmp causing fsysdep_catch (a macro) to
- return FALSE. We handle that here. If read can be interrupted,
- fsysdep_catch will be defined to TRUE. */
- if (fsysdep_catch ())
- {
- /* Prepare to catch SIGALRM and schedule the signal. */
- usysdep_start_catch ();
- usset_signal (SIGALRM, usalarm, TRUE, (boolean *) NULL);
- alarm (ctimeout);
- }
- else
- {
- /* We caught a signal. We don't actually have to do anything,
- as all the appropriate checks are made at the start of the
- following loop. */
- }
-
- fret = FALSE;
-
- cwouldblock = 0;
- while (TRUE)
- {
- int cgot;
-
- #if HAVE_SYSV_TERMIO || HAVE_POSIX_TERMIOS
- /* If we can tell the terminal not to return until we have a
- certain number of characters, do so. */
- if (q->fterminal)
- {
- int csetmin;
-
- /* I'm not that confident about setting MIN to values larger
- than 127, although up to 255 would probably work. */
- if (cmin < 127)
- csetmin = cmin;
- else
- csetmin = 127;
-
- if (csetmin != cSmin)
- {
- q->snew.c_cc[VMIN] = csetmin;
- while (! fsetterminfo (q->o, &q->snew))
- {
- if (errno != EINTR
- || FGOT_QUIT_SIGNAL ())
- {
- int ierr;
-
- /* We turn off the signal before reporting the
- error to minimize any problems with
- interrupted system calls. */
- ierr = errno;
- usset_signal (SIGALRM, SIG_IGN, TRUE, (boolean *) NULL);
- alarm (0);
- usysdep_end_catch ();
- ulog (LOG_ERROR, "Can't set MIN for terminal: %s",
- strerror (ierr));
- return FALSE;
- }
-
- if (fSalarm)
- {
- ulog (LOG_ERROR,
- "Timed out when setting MIN to %d; retrying",
- csetmin);
- fSalarm = FALSE;
- alarm (ctimeout);
- }
- }
- cSmin = csetmin;
- }
- }
- #endif /* HAVE_SYSV_TERMIO || HAVE_POSIX_TERMIOS */
-
- /* If we've received a signal, get out now. */
- if (FGOT_QUIT_SIGNAL ())
- break;
-
- /* If we've already gotten a SIGALRM, get out with whatever
- we've accumulated. */
- if (fSalarm)
- {
- fret = TRUE;
- break;
- }
-
- /* Right here is the race condition which we avoid by having the
- SIGALRM handler schedule another SIGALRM. */
- #if HAVE_TLI
- if (q->ftli)
- {
- int iflags;
-
- cgot = t_rcv (q->o, zbuf, cwant, &iflags);
- if (cgot < 0 && t_errno != TSYSERR)
- {
- usset_signal (SIGALRM, SIG_IGN, TRUE, (boolean *) NULL);
- alarm (0);
- usysdep_end_catch ();
-
- if (freport)
- ulog (LOG_ERROR, "t_rcv: %s",
- (t_errno >= 0 && t_errno < t_nerr
- ? t_errlist[t_errno]
- : "unknown TLI error"));
-
- return FALSE;
- }
- }
- else
- #endif
- cgot = read (q->o, zbuf, cwant);
-
- /* If the read returned an error, check for signals. */
- if (cgot < 0)
- {
- if (errno == EINTR)
- {
- /* Log the signal. */
- ulog (LOG_ERROR, (const char *) NULL);
- }
- if (fSalarm)
- {
- fret = TRUE;
- break;
- }
- if (FGOT_QUIT_SIGNAL ())
- break;
- }
-
- /* If read returned an error, get out. We just ignore EINTR
- here, since it must be from some signal we don't care about.
- If the read returned 0 then the line must have been hung up
- (normally we would have received SIGHUP, but we can't count
- on that). We turn off the signals before calling ulog to
- reduce problems with interrupted system calls. */
- if (cgot > 0)
- cwouldblock = 0;
- else
- {
- if (cgot < 0 && errno == EINTR)
- cgot = 0;
- else if (cgot < 0
- && (errno == EAGAIN || errno == EWOULDBLOCK)
- && cwouldblock < 2)
- {
- /* Incomprehensibly, on some systems the read will
- return EWOULDBLOCK even though the descriptor has
- been set to blocking mode. We permit the read call
- to do this twice in a row, and then error out. We
- don't want to permit an arbitrary number of
- EWOULDBLOCK errors, since that could hang us up
- indefinitely. */
- ++cwouldblock;
- cgot = 0;
- }
- else
- {
- int ierr;
-
- ierr = errno;
-
- usset_signal (SIGALRM, SIG_IGN, TRUE, (boolean *) NULL);
- alarm (0);
- usysdep_end_catch ();
-
- if (freport)
- {
- if (cgot == 0)
- ulog (LOG_ERROR, "Line disconnected");
- else
- ulog (LOG_ERROR, "read: %s", strerror (ierr));
- }
-
- return FALSE;
- }
- }
-
- cwant -= cgot;
- if (cgot >= cmin)
- cmin = 0;
- else
- cmin -= cgot;
- zbuf += cgot;
- *pclen += cgot;
-
- /* If we have enough data, get out now. */
- if (cmin == 0)
- {
- fret = TRUE;
- break;
- }
-
- #if HAVE_BSD_TTY
- /* We still want more data, so sleep long enough for the rest of
- it to arrive. We don't this for System V or POSIX because
- setting MIN is good enough (we can't sleep longer than it
- takes to get MAX_INPUT characters anyhow).
-
- The baud rate is approximately 10 times the number of
- characters which will arrive in one second, so the number of
- milliseconds to sleep ==
- characters * (milliseconds / character) ==
- characters * (1000 * (seconds / character)) ==
- characters * (1000 * (1 / (baud / 10))) ==
- characters * (10000 / baud)
-
- We arbitrarily reduce the sleep amount by 10 milliseconds to
- attempt to account for the amount of time it takes to set up
- the sleep. This is how long it takes to get half a character
- at 19200 baud. We then don't bother to sleep for less than
- 10 milliseconds. We don't sleep if the read was interrupted.
-
- We use select to sleep. It would be easy to use poll as
- well, but it's unlikely that any system with BSD ttys would
- have poll but not select. Using select avoids hassles with
- the pending SIGALRM; if it hits the select will be
- interrupted, and otherwise the select will not affect it. */
-
- #if ! HAVE_SELECT
- #error This code requires select; feel free to extend it
- #endif
-
- if (q->fterminal && cmin > 1 && cgot > 0)
- {
- int csleepchars;
- int isleep;
-
- /* We don't try to read all the way up to MAX_INPUT,
- since that might drop a character. */
- if (cmin <= MAX_INPUT - 10)
- csleepchars = cmin;
- else
- csleepchars = MAX_INPUT - 10;
-
- isleep = (int) (((long) csleepchars * 10000L) / q->ibaud);
- isleep -= 10;
-
- if (isleep > 10)
- {
- struct timeval s;
-
- s.tv_sec = isleep / 1000;
- s.tv_usec = (isleep % 1000) * 1000;
-
- /* Some versions of select take a pointer to an int,
- while some take a pointer to an fd_set. I just cast
- the arguments to a generic pointer, and assume that
- any machine which distinguishes int * from fd_set *
- (I would be amazed if there are any such machines)
- have an appropriate prototype somewhere or other. */
- (void) select (0, (pointer) NULL, (pointer) NULL,
- (pointer) NULL, &s);
-
- /* Here either the select finished sleeping or we got a
- SIGALRM. If the latter occurred, fSalarm was set to
- TRUE; it will be checked at the top of the loop. */
- }
- }
- #endif /* HAVE_BSD_TTY */
- }
-
- /* Turn off the pending SIGALRM and return. */
-
- usset_signal (SIGALRM, SIG_IGN, TRUE, (boolean *) NULL);
- alarm (0);
- usysdep_end_catch ();
-
- return fret;
- }
-
- /* Read from a port with separate read/write file descriptors. */
-
- boolean
- fsdouble_read (qconn, zbuf, pclen, cmin, ctimeout, freport)
- struct sconnection *qconn;
- char *zbuf;
- size_t *pclen;
- size_t cmin;
- int ctimeout;
- boolean freport;
- {
- struct ssysdep_conn *qsysdep;
-
- qsysdep = (struct ssysdep_conn *) qconn->psysdep;
- qsysdep->o = qsysdep->ord;
- return fsysdep_conn_read (qconn, zbuf, pclen, cmin, ctimeout, freport);
- }
-
- /* Write data to a connection. This routine handles all types of
- connections, including TLI. */
-
- boolean
- fsysdep_conn_write (qconn, zwrite, cwrite)
- struct sconnection *qconn;
- const char *zwrite;
- size_t cwrite;
- {
- struct ssysdep_conn *q;
- int czero;
-
- q = (struct ssysdep_conn *) qconn->psysdep;
-
- /* We want blocking writes here. */
- if (! fsblock (q, TRUE))
- return FALSE;
-
- czero = 0;
-
- while (cwrite > 0)
- {
- int cdid;
-
- /* Loop until we don't get an interrupt. */
- while (TRUE)
- {
- /* If we've received a signal, don't continue. */
- if (FGOT_QUIT_SIGNAL ())
- return FALSE;
-
- #if HAVE_TLI
- if (q->ftli)
- {
- cdid = t_snd (q->o, (char *) zwrite, cwrite, 0);
- if (cdid < 0 && t_errno != TSYSERR)
- {
- ulog (LOG_ERROR, "t_snd: %s",
- (t_errno >= 0 && t_errno < t_nerr
- ? t_errlist[t_errno]
- : "unknown TLI error"));
- return FALSE;
- }
- }
- else
- #endif
- cdid = write (q->o, zwrite, cwrite);
-
- if (cdid >= 0)
- break;
- if (errno != EINTR)
- break;
-
- /* We were interrupted by a signal. Log it. */
- ulog (LOG_ERROR, (const char *) NULL);
- }
-
- if (cdid < 0)
- {
- if (errno != EAGAIN && errno != EWOULDBLOCK && errno != ENODATA)
- {
- ulog (LOG_ERROR, "write: %s", strerror (errno));
- return FALSE;
- }
- cdid = 0;
- }
-
- if (cdid == 0)
- {
- /* On some systems write will return 0 if carrier is lost.
- If we fail to write anything ten times in a row, we
- assume that this has happened. This is hacked in like
- this because there seems to be no reliable way to tell
- exactly why the write returned 0. */
- ++czero;
- if (czero >= 10)
- {
- ulog (LOG_ERROR, "Line disconnected");
- return FALSE;
- }
- }
- else
- {
- czero = 0;
-
- cwrite -= cdid;
- zwrite += cdid;
- }
- }
-
- return TRUE;
- }
-
- /* Write to a port with separate read/write file descriptors. */
-
- boolean
- fsdouble_write (qconn, zwrite, cwrite)
- struct sconnection *qconn;
- const char *zwrite;
- size_t cwrite;
- {
- struct ssysdep_conn *qsysdep;
-
- qsysdep = (struct ssysdep_conn *) qconn->psysdep;
- qsysdep->o = qsysdep->ord;
- if (! fsblock (qsysdep, TRUE))
- return FALSE;
- qsysdep->o = qsysdep->owr;
- return fsysdep_conn_write (qconn, zwrite, cwrite);
- }
-
- /* The fsysdep_conn_io routine is supposed to both read and write data
- until it has either filled its read buffer or written out all the
- data it was given. This lets us write out large packets without
- losing incoming data. It handles all types of connections,
- including TLI. */
-
- boolean
- fsysdep_conn_io (qconn, zwrite, pcwrite, zread, pcread)
- struct sconnection *qconn;
- const char *zwrite;
- size_t *pcwrite;
- char *zread;
- size_t *pcread;
- {
- struct ssysdep_conn *q;
- size_t cwrite, cread;
- int czero;
-
- q = (struct ssysdep_conn *) qconn->psysdep;
-
- cwrite = *pcwrite;
- *pcwrite = 0;
- cread = *pcread;
- *pcread = 0;
-
- czero = 0;
-
- while (TRUE)
- {
- int cgot, cdid;
- size_t cdo;
-
- /* This used to always use nonblocking writes, but it turns out
- that some systems don't support them on terminals.
-
- The current algorithm is:
- loop:
- unblocked read
- if read buffer full, return
- if nothing to write, return
- if HAVE_UNBLOCKED_WRITES
- write all data
- else
- write up to SINGLE_WRITE bytes
- if all data written, return
- if no data written
- if select works
- select on the write descriptor with a ten second timeout
- else
- blocked write of one byte with a ten second alarm
-
- This algorithm should work whether the system supports
- unblocked writes on terminals or not. If the system supports
- unblocked writes but HAVE_UNBLOCKED_WRITES is 0, then it will
- call write more often than it needs to. If the system does
- not support unblocked writes but HAVE_UNBLOCKED_WRITES is 1,
- then the write may hang so long that incoming data is lost.
- This is actually possible at high baud rates on any system
- when a blocking write is done; there is no solution, except
- hardware handshaking.
-
- If we were not able to write any data, then we need to block
- until we can write something. The code used to simply do a
- blocking write. However, that fails when a bidirectional
- protocol is permitted to push out enough bytes to fill the
- entire pipe between the two communicating uucico processes.
- They can both block on writing, because neither is reading.
-
- In this case, we use select. We could select on both the
- read and write descriptor, but on some systems that would
- lead to calling read on each byte, which would be very
- inefficient. Instead, we select only on the write
- descriptor. After the select succeeds or times out, we retry
- the read.
-
- Of course, some systems don't have select, and on some
- systems that have it it doesn't work on terminal devices. If
- we can't use select, then we do a blocked write of a single
- byte after setting an alarm. We only write a single byte to
- avoid any confusion as to whether or not the byte was
- actually written. */
-
- /* If we are running on standard input, we switch the file
- descriptors by hand. */
- if (q->ord >= 0)
- q->o = q->ord;
-
- /* Do an unblocked read. */
- if (! fsblock (q, FALSE))
- return FALSE;
-
- /* Loop until we get something (error or data) other than an
- acceptable EINTR. */
- while (TRUE)
- {
- /* If we've received a signal, don't continue. */
- if (FGOT_QUIT_SIGNAL ())
- return FALSE;
-
- #if HAVE_TLI
- if (q->ftli)
- {
- int iflags;
-
- cgot = t_rcv (q->o, zread, cread, &iflags);
- if (cgot < 0)
- {
- if (t_errno == TNODATA)
- errno = EAGAIN;
- else if (t_errno != TSYSERR)
- {
- ulog (LOG_ERROR, "t_rcv: %s",
- (t_errno >= 0 && t_errno < t_nerr
- ? t_errlist[t_errno]
- : "unknown TLI error"));
- return FALSE;
- }
- }
- }
- else
- #endif
- cgot = read (q->o, zread, cread);
-
- if (cgot >= 0)
- break;
- if (errno != EINTR)
- break;
-
- /* We got interrupted by a signal. Log it. */
- ulog (LOG_ERROR, (const char *) NULL);
- }
-
- if (cgot < 0)
- {
- if (errno != EAGAIN && errno != EWOULDBLOCK && errno != ENODATA)
- {
- ulog (LOG_ERROR, "read: %s", strerror (errno));
- return FALSE;
- }
- cgot = 0;
- }
-
- cread -= cgot;
- zread += cgot;
- *pcread += cgot;
-
- /* If we've filled the read buffer, or we have nothing left to
- write, return out. */
- if (cread == 0 || cwrite == 0)
- return TRUE;
-
- /* The port is currently unblocked. Do a write. */
- cdo = cwrite;
-
- #if ! HAVE_UNBLOCKED_WRITES
- if (q->fterminal && cdo > SINGLE_WRITE)
- cdo = SINGLE_WRITE;
- #endif
-
- if (q->owr >= 0)
- q->o = q->owr;
-
- /* Loop until we get something besides EINTR. */
- while (TRUE)
- {
- /* If we've received a signal, don't continue. */
- if (FGOT_QUIT_SIGNAL ())
- return FALSE;
-
- #if HAVE_TLI
- if (q->ftli)
- {
- cdid = t_snd (q->o, (char *) zwrite, cdo, 0);
- if (cdid < 0)
- {
- if (t_errno == TFLOW)
- errno = EAGAIN;
- else if (t_errno != TSYSERR)
- {
- ulog (LOG_ERROR, "t_snd: %s",
- (t_errno >= 0 && t_errno < t_nerr
- ? t_errlist[t_errno]
- : "unknown TLI error"));
- return FALSE;
- }
- }
- }
- else
- #endif
- cdid = write (q->o, zwrite, cdo);
-
- if (cdid >= 0)
- break;
- if (errno != EINTR)
- break;
-
- /* We got interrupted by a signal. Log it. */
- ulog (LOG_ERROR, (const char *) NULL);
- }
-
- if (cdid < 0)
- {
- if (errno != EAGAIN && errno != EWOULDBLOCK && errno != ENODATA)
- {
- ulog (LOG_ERROR, "write: %s", strerror (errno));
- return FALSE;
- }
- cdid = 0;
- }
-
- if (cdid > 0)
- {
- /* We wrote some data. If we wrote everything, return out.
- Otherwise loop around and do another read. */
- cwrite -= cdid;
- zwrite += cdid;
- *pcwrite += cdid;
-
- if (cwrite == 0)
- return TRUE;
-
- czero = 0;
- }
- else
- {
- #if HAVE_SELECT
- struct timeval stime;
- long recvC;
- int imask;
- int c;
-
- /* We didn't write any data. Call select. We use a timeout
- long enough for recvC bytes to be received
- secs delay == (recvC bytes * 10 bits/byte) / baud bits/sec
- usecs delay == (((recvC bytes * 1000000 usecs/sec)
- / baud bits/sec)
- * 10 bits/byte)
- use recvC so we never wait for longer than 2Kb, otherwise
- we would overflow the size of a long in the usec calc.
- */
- recvC = cread; if (recvC >= 2048) recvC = 2048;
- stime.tv_sec = recvC * 10L / (long)q->ibaud;
- stime.tv_usec = (((recvC * 1000000L / (long) q->ibaud) * 10L)
- % 1000000L);
-
- imask = 1 << q->o;
- if (imask == 0)
- ulog (LOG_FATAL, "fsysdep_conn_io: File descriptors too large");
-
- /* If we've received a signal, don't continue. */
- if (FGOT_QUIT_SIGNAL ())
- return FALSE;
-
- DEBUG_MESSAGE0 (DEBUG_PORT, "fsysdep_conn_io: Calling select");
-
- /* We don't bother to loop on EINTR. If we get a signal, we
- just loop around and try the read and write again. */
- c = select (q->o + 1, (pointer) NULL, (pointer) &imask,
- (pointer) NULL, &stime);
- if (c < 0 && errno == EINTR)
- {
- /* We got interrupted by a signal. Log it. */
- ulog (LOG_ERROR, (const char *) NULL);
- }
- else if (c >= 0)
- {
- /* The select either discovered that we could write
- something, or it timed out. Either way, we go around
- the main read/write loop again. */
- }
- else
- #endif /* HAVE_SELECT */
- {
- int ierr;
-
- /* Either the select failed for some reason other than
- EINTR, or the system does not support select at all.
- Fall back on a timed write. We don't worry about why
- the select might have failed, we just assume that it
- will not succeed on this descriptor. */
-
- #if HAVE_RESTARTABLE_SYSCALLS
- /* If HAVE_RESTARTABLE_SYSCALLS, then receiving an alarm
- signal in the middle of a write will not cause the
- write to return EINTR, and the only way to interrupt
- the write is to longjmp out of it (see sysh.unx).
- That is unreliable, because it means that we won't
- know whether the byte was actually written or not.
- However, I believe that the only system on which we
- need to do this longjmp is BSD 4.2, and that system
- supports select, so we should never execute this
- case. */
- ulog (LOG_FATAL, "fsysdep_conn_io: Unsupported case; see code");
- #endif
-
- if (q->ord >= 0)
- q->o = q->ord;
-
- if (! fsblock (q, TRUE))
- return FALSE;
-
- DEBUG_MESSAGE0 (DEBUG_PORT, "fsysdep_conn_io: Blocking write");
-
- if (q->owr >= 0)
- q->o = q->owr;
-
- /* If we've received a signal, don't continue. */
- if (FGOT_QUIT_SIGNAL ())
- return FALSE;
-
- /* Start up an alarm to interrupt the write. Note that
- we don't need to use the catch stuff, since we know
- that HAVE_RESTARTABLE_SYSCALLS is 0. */
- usset_signal (SIGALRM, usalarm, TRUE, (boolean *) NULL);
- alarm ((int) ((long) 10240 / q->ibaud) + 1);
-
- /* There is a race condition here: on a severely loaded
- system, we could get the alarm before we start the
- write call. This would not be a disaster; often the
- write will succeed anyhow. */
- #if HAVE_TLI
- if (q->ftli)
- {
- cdid = t_snd (q->o, (char *) zwrite, 1, 0);
- if (cdid < 0 && t_errno != TSYSERR)
- {
- usset_signal (SIGALRM, SIG_IGN, TRUE, (boolean *) NULL);
- alarm (0);
- ulog (LOG_ERROR, "t_snd: %s",
- (t_errno >= 0 && t_errno < t_nerr
- ? t_errlist[t_errno]
- : "unknown TLI error"));
- return FALSE;
- }
- }
- else
- #endif
- cdid = write (q->o, zwrite, 1);
-
- ierr = errno;
-
- /* Note that we don't really care whether the write
- finished because the byte was written out or whether
- it finished because the alarm was triggered. Either
- way, we are going to loop around and try another
- read. */
-
- usset_signal (SIGALRM, SIG_IGN, TRUE, (boolean *) NULL);
- alarm (0);
-
- if (cdid < 0)
- {
- if (ierr == EINTR)
- {
- /* We got interrupted by a signal. Log it. */
- ulog (LOG_ERROR, (const char *) NULL);
- }
- else
- {
- ulog (LOG_ERROR, "write: %s", strerror (ierr));
- return FALSE;
- }
- }
- else if (cdid == 0)
- {
- /* On some systems write will return 0 if carrier is
- lost. If we fail to write anything ten times in
- a row, we assume that this has happened. This is
- hacked in like this because there seems to be no
- reliable way to tell exactly why the write
- returned 0. */
- ++czero;
- if (czero >= 10)
- {
- ulog (LOG_ERROR, "Line disconnected");
- return FALSE;
- }
- }
- else
- {
- cwrite -= cdid;
- zwrite += cdid;
- *pcwrite += cdid;
- czero = 0;
- }
- }
- }
- }
- }
-
- /* Send a break character to a serial port. */
-
- static boolean
- fsserial_break (qconn)
- struct sconnection *qconn;
- {
- struct ssysdep_conn *q;
-
- q = (struct ssysdep_conn *) qconn->psysdep;
-
- #if HAVE_BSD_TTY
- (void) ioctl (q->o, TIOCSBRK, 0);
- sleep (2);
- (void) ioctl (q->o, TIOCCBRK, 0);
- return TRUE;
- #endif /* HAVE_BSD_TTY */
- #if HAVE_SYSV_TERMIO
- (void) ioctl (q->o, TCSBRK, 0);
- return TRUE;
- #endif /* HAVE_SYSV_TERMIO */
- #if HAVE_POSIX_TERMIOS
- return tcsendbreak (q->o, 0) == 0;
- #endif /* HAVE_POSIX_TERMIOS */
- }
-
- /* Send a break character to a stdin port. */
-
- static boolean
- fsstdin_break (qconn)
- struct sconnection *qconn;
- {
- struct ssysdep_conn *qsysdep;
-
- qsysdep = (struct ssysdep_conn *) qconn->psysdep;
- qsysdep->o = qsysdep->owr;
- return fsserial_break (qconn);
- }
-
- /* Change the setting of a serial port. */
-
- /*ARGSUSED*/
- static boolean
- fsserial_set (qconn, tparity, tstrip, txonxoff)
- struct sconnection *qconn;
- enum tparitysetting tparity;
- enum tstripsetting tstrip;
- enum txonxoffsetting txonxoff;
- {
- register struct ssysdep_conn *q;
- boolean fchanged, fdo;
- int iset = 0;
- int iclear = 0;
-
- q = (struct ssysdep_conn *) qconn->psysdep;
-
- if (! q->fterminal)
- return TRUE;
-
- fchanged = FALSE;
-
- /* Set the parity for output characters. */
-
- #if HAVE_BSD_TTY
-
- /* This will also cause parity detection on input characters. */
-
- fdo = FALSE;
- switch (tparity)
- {
- case PARITYSETTING_DEFAULT:
- break;
- case PARITYSETTING_NONE:
- #if HAVE_PARITY_BUG
- /* The Sony NEWS mishandles this for some reason. */
- iset = 0;
- iclear = ANYP;
- #else
- iset = ANYP;
- iclear = 0;
- #endif
- fdo = TRUE;
- break;
- case PARITYSETTING_EVEN:
- iset = EVENP;
- iclear = ODDP;
- fdo = TRUE;
- break;
- case PARITYSETTING_ODD:
- iset = ODDP;
- iclear = EVENP;
- fdo = TRUE;
- break;
- case PARITYSETTING_MARK:
- case PARITYSETTING_SPACE:
- /* Not supported. */
- break;
- }
-
- if (fdo)
- {
- if ((q->snew.stty.sg_flags & iset) != iset
- || (q->snew.stty.sg_flags & iclear) != 0)
- {
- q->snew.stty.sg_flags |= iset;
- q->snew.stty.sg_flags &=~ iclear;
- fchanged = TRUE;
- }
- }
-
- #else /* ! HAVE_BSD_TTY */
-
- fdo = FALSE;
- switch (tparity)
- {
- case PARITYSETTING_DEFAULT:
- break;
- case PARITYSETTING_NONE:
- iset = CS8;
- iclear = PARENB | PARODD | (CSIZE &~ CS8);
- fdo = TRUE;
- break;
- case PARITYSETTING_EVEN:
- iset = PARENB | CS7;
- iclear = PARODD | (CSIZE &~ CS7);
- fdo = TRUE;
- break;
- case PARITYSETTING_ODD:
- iset = PARENB | PARODD | CS7;
- iclear = CSIZE &~ CS7;
- fdo = TRUE;
- break;
- case PARITYSETTING_MARK:
- case PARITYSETTING_SPACE:
- /* Not supported. */
- break;
- }
-
- if (fdo)
- {
- if ((q->snew.c_cflag & iset) != iset
- || (q->snew.c_cflag & iclear) != 0)
- {
- q->snew.c_cflag |= iset;
- q->snew.c_cflag &=~ iclear;
- fchanged = TRUE;
- }
- }
-
- #endif /* ! HAVE_BSD_TTY */
-
- /* Set whether input characters are stripped to seven bits. */
-
- #if HAVE_BSD_TTY
-
- #ifdef LPASS8
- {
- int i;
-
- i = LPASS8;
- if (tstrip == STRIPSETTING_EIGHTBITS)
- {
- i = LPASS8;
- (void) ioctl (q->o, TIOCLBIS, &i);
- }
- else if (tstrip == STRIPSETTING_SEVENBITS)
- {
- i = LPASS8;
- (void) ioctl (q->o, TIOCLBIC, &i);
- }
- }
- #endif
-
- #else /* ! HAVE_BSD_TTY */
-
- fdo = FALSE;
- switch (tstrip)
- {
- case STRIPSETTING_DEFAULT:
- break;
- case STRIPSETTING_EIGHTBITS:
- iset = 0;
- iclear = ISTRIP;
- fdo = TRUE;
- break;
- case STRIPSETTING_SEVENBITS:
- iset = ISTRIP;
- iclear = 0;
- fdo = TRUE;
- break;
- }
-
- if (fdo)
- {
- if ((q->snew.c_iflag & iset) != iset
- || (q->snew.c_iflag & iclear) != 0)
- {
- q->snew.c_iflag |= iset;
- q->snew.c_iflag &=~ iclear;
- fchanged = TRUE;
- }
- }
-
- #endif /* ! HAVE_BSD_TTY */
-
- /* Set XON/XOFF handshaking. */
-
- #if HAVE_BSD_TTY
-
- fdo = FALSE;
- switch (txonxoff)
- {
- case XONXOFF_DEFAULT:
- break;
- case XONXOFF_OFF:
- iset = RAW;
- iclear = TANDEM | CBREAK;
- fdo = TRUE;
- break;
- case XONXOFF_ON:
- iset = CBREAK | TANDEM;
- iclear = RAW;
- fdo = TRUE;
- break;
- }
-
- if (fdo)
- {
- if ((q->snew.stty.sg_flags & iset) != iset
- || (q->snew.stty.sg_flags & iclear) != 0)
- {
- q->snew.stty.sg_flags |= iset;
- q->snew.stty.sg_flags &=~ iclear;
- fchanged = TRUE;
- }
- }
-
- #else /* ! HAVE_BSD_TTY */
-
- fdo = FALSE;
- switch (txonxoff)
- {
- case XONXOFF_DEFAULT:
- break;
- case XONXOFF_OFF:
- iset = 0;
- iclear = IXON | IXOFF;
- fdo = TRUE;
- break;
- case XONXOFF_ON:
- #ifdef CRTSCTS
- #if HAVE_POSIX_TERMIOS
- /* This is system dependent, but I haven't figured out a good
- way around it yet. If we are doing hardware flow control, we
- don't send XON/XOFF characters but we do recognize them. */
- if ((q->snew.c_cflag & CRTSCTS) != 0)
- {
- iset = IXON;
- iclear = IXOFF;
- fdo = TRUE;
- break;
- }
- #endif /* HAVE_POSIX_TERMIOS */
- #endif /* defined (CRTSCTS) */
- #ifdef CRTSFL
- if ((q->snew.c_cflag & CRTSFL) != 0)
- {
- iset = IXON;
- iclear = IXOFF;
- /* SCO says we cant have CRTSFL **and** RTSFLOW/CTSFLOW */
- #ifdef RTSFLOW
- iclear |= RTSFLOW;
- #endif
- #ifdef CTSFLOW
- iclear |= CTSFLOW;
- #endif
- fdo = TRUE;
- break;
- }
- #endif /* defined(CRTSFL) */
- iset = IXON | IXOFF;
- iclear = 0;
- fdo = TRUE;
- break;
- }
-
- if (fdo)
- {
- if ((q->snew.c_iflag & iset) != iset
- || (q->snew.c_iflag & iclear) != 0)
- {
- q->snew.c_iflag |= iset;
- q->snew.c_iflag &=~ iclear;
- fchanged = TRUE;
- }
- }
-
- #endif /* ! HAVE_BSD_TTY */
-
- if (fchanged)
- {
- if (! fsetterminfodrain (q->o, &q->snew))
- {
- ulog (LOG_ERROR, "Can't change terminal settings: %s",
- strerror (errno));
- return FALSE;
- }
- }
-
- #if HAVE_BSD_TTY
- if (txonxoff == XONXOFF_ON
- && (q->snew.stty.sg_flags & ANYP) == ANYP)
- {
- int i;
-
- /* At least on Ultrix, we seem to have to set LLITOUT and
- LPASS8. This shouldn't foul things up anywhere else. As far
- as I can tell, this has to be done after setting the terminal
- into cbreak mode, not before. */
- #ifndef LLITOUT
- #define LLITOUT 0
- #endif
- #ifndef LPASS8
- #define LPASS8 0
- #endif
- #ifndef LAUTOFLOW
- #define LAUTOFLOW 0
- #endif
- i = LLITOUT | LPASS8 | LAUTOFLOW;
- (void) ioctl (q->o, TIOCLBIS, &i);
-
- #if HAVE_STRIP_BUG
- /* Ultrix 4.0 has a peculiar problem: setting CBREAK always
- causes input characters to be stripped. I hope this does not
- apply to other BSD systems. It is possible to work around
- this by using the termio call. I wish this sort of stuff was
- not necessary!!! */
- {
- struct termio s;
-
- if (ioctl (q->o, TCGETA, &s) >= 0)
- {
- s.c_iflag &=~ ISTRIP;
- (void) ioctl (q->o, TCSETA, &s);
- }
- }
- #endif /* HAVE_STRIP_BUG */
- }
- #endif /* HAVE_BSD_TTY */
-
- return TRUE;
- }
-
- /* Change settings of a stdin port. */
-
- static boolean
- fsstdin_set (qconn, tparity, tstrip, txonxoff)
- struct sconnection *qconn;
- enum tparitysetting tparity;
- enum tstripsetting tstrip;
- enum txonxoffsetting txonxoff;
- {
- struct ssysdep_conn *qsysdep;
-
- qsysdep = (struct ssysdep_conn *) qconn->psysdep;
- qsysdep->o = qsysdep->ord;
- return fsserial_set (qconn, tparity, tstrip, txonxoff);
- }
-
- /* Run a chat program. */
-
- static boolean
- fsrun_chat (oread, owrite, pzprog)
- int oread;
- int owrite;
- char **pzprog;
- {
- int aidescs[3];
- FILE *e;
- pid_t ipid;
- char *z;
- size_t c;
-
- aidescs[0] = oread;
- aidescs[1] = owrite;
- aidescs[2] = SPAWN_READ_PIPE;
-
- /* Pass fkeepuid, fkeepenv and fshell as TRUE. This puts the
- responsibility of maintaing security on the chat program. */
- ipid = ixsspawn ((const char **) pzprog, aidescs, TRUE, TRUE,
- (const char *) NULL, FALSE, TRUE, (const char *) NULL,
- (const char *) NULL, (const char *) NULL);
- if (ipid < 0)
- {
- ulog (LOG_ERROR, "ixsspawn (%s): %s", pzprog[0], strerror (errno));
- return FALSE;
- }
-
- e = fdopen (aidescs[2], (char *) "r");
- if (e == NULL)
- {
- ulog (LOG_ERROR, "fdopen: %s", strerror (errno));
- (void) close (aidescs[2]);
- (void) kill (ipid, SIGKILL);
- (void) ixswait ((unsigned long) ipid, (const char *) NULL);
- return FALSE;
- }
-
- /* The FILE e now is attached to stderr of the program. Forward
- every line the program outputs to the log file. */
- z = NULL;
- c = 0;
- while (getline (&z, &c, e) > 0)
- {
- size_t clen;
-
- clen = strlen (z);
- if (z[clen - 1] == '\n')
- z[clen - 1] = '\0';
- if (*z != '\0')
- ulog (LOG_NORMAL, "chat: %s", z);
- }
-
- xfree ((pointer) z);
- (void) fclose (e);
-
- return ixswait ((unsigned long) ipid, "Chat program") == 0;
- }
-
- /* Run a chat program on a port using separate read/write file
- descriptors. */
-
- boolean
- fsdouble_chat (qconn, pzprog)
- struct sconnection *qconn;
- char **pzprog;
- {
- struct ssysdep_conn *qsysdep;
- boolean fret;
-
- qsysdep = (struct ssysdep_conn *) qconn->psysdep;
- fret = fsrun_chat (qsysdep->ord, qsysdep->owr, pzprog);
- if (qsysdep->fterminal)
- (void) fgetterminfo (qsysdep->ord, &qsysdep->snew);
- return fret;
- }
-
- /* Run a chat program on any general type of connection. */
-
- boolean
- fsysdep_conn_chat (qconn, pzprog)
- struct sconnection *qconn;
- char **pzprog;
- {
- struct ssysdep_conn *qsysdep;
- boolean fret;
-
- qsysdep = (struct ssysdep_conn *) qconn->psysdep;
- fret = fsrun_chat (qsysdep->o, qsysdep->o, pzprog);
- if (qsysdep->fterminal)
- (void) fgetterminfo (qsysdep->o, &qsysdep->snew);
- return fret;
- }
-
- /* Return baud rate of a serial port. */
-
- static long
- isserial_baud (qconn)
- struct sconnection *qconn;
- {
- struct ssysdep_conn *qsysdep;
-
- qsysdep = (struct ssysdep_conn *) qconn->psysdep;
- return qsysdep->ibaud;
- }
-